diff --git a/config/stl-headers b/config/stl-headers index 723c42b33b..d4786f1c99 100644 --- a/config/stl-headers +++ b/config/stl-headers @@ -34,6 +34,7 @@ ostream set stack string +type_traits utility vector cassert diff --git a/config/system-headers b/config/system-headers index 811086b63a..d3d282931c 100644 --- a/config/system-headers +++ b/config/system-headers @@ -1114,6 +1114,7 @@ ToolUtils.h tr1/functional trace.h Traps.h +type_traits typeinfo types.h Types.h diff --git a/docshell/base/crashtests/1257730-1.html b/docshell/base/crashtests/1257730-1.html new file mode 100644 index 0000000000..028a1adb88 --- /dev/null +++ b/docshell/base/crashtests/1257730-1.html @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/docshell/base/crashtests/crashtests.list b/docshell/base/crashtests/crashtests.list index aab882f152..2a1918d5f7 100644 --- a/docshell/base/crashtests/crashtests.list +++ b/docshell/base/crashtests/crashtests.list @@ -12,3 +12,4 @@ load 514779-1.xhtml load 614499-1.html load 678872-1.html skip-if(Android||B2G) pref(dom.disable_open_during_load,false) load 914521.html +pref(browser.send_pings,true) load 1257730-1.html diff --git a/dom/apps/Langpacks.jsm b/dom/apps/Langpacks.jsm index bf26a67e9a..ff0df80b52 100644 --- a/dom/apps/Langpacks.jsm +++ b/dom/apps/Langpacks.jsm @@ -18,7 +18,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "ppmm", this.EXPORTED_SYMBOLS = ["Langpacks"]; -let debug; +var debug; function debugPrefObserver() { debug = Services.prefs.getBoolPref("dom.mozApps.debug") ? (aMsg) => { diff --git a/dom/base/crashtests/729431-1.xhtml b/dom/base/crashtests/729431-1.xhtml new file mode 100644 index 0000000000..a9c93aa2e0 --- /dev/null +++ b/dom/base/crashtests/729431-1.xhtml @@ -0,0 +1,36 @@ + + + + + + + diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 03b0aed11b..40e97faee8 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -148,6 +148,7 @@ load 709954.html load 713417-1.html load 713417-2.html load 715056.html +load 729431-1.xhtml load 741163-1.html load 745495.html load 752226-1.html diff --git a/dom/base/test/browser_use_counters.js b/dom/base/test/browser_use_counters.js index 80341bb2f4..0b1e36c692 100644 --- a/dom/base/test/browser_use_counters.js +++ b/dom/base/test/browser_use_counters.js @@ -172,7 +172,7 @@ var check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix } }); -let check_use_counter_img = Task.async(function* (file, use_counter_middlefix) { +var check_use_counter_img = Task.async(function* (file, use_counter_middlefix) { info("checking " + file + " as image with histogram " + use_counter_middlefix); let newTab = gBrowser.addTab("about:blank"); @@ -230,7 +230,7 @@ let check_use_counter_img = Task.async(function* (file, use_counter_middlefix) { "document counts " + use_counter_middlefix + " after are correct"); }); -let check_use_counter_direct = Task.async(function* (file, use_counter_middlefix, xfail=false) { +var check_use_counter_direct = Task.async(function* (file, use_counter_middlefix, xfail=false) { info("checking " + file + " with histogram " + use_counter_middlefix); let newTab = gBrowser.addTab( "about:blank"); diff --git a/dom/canvas/test/reftest/filters/global-alpha.html b/dom/canvas/test/reftest/filters/global-alpha.html index 4676e00605..8b6eb97520 100644 --- a/dom/canvas/test/reftest/filters/global-alpha.html +++ b/dom/canvas/test/reftest/filters/global-alpha.html @@ -7,7 +7,7 @@ var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); -ctx.filter = 'drop-shadow(0 10px black)'; +ctx.filter = 'drop-shadow(0 10px #000)'; ctx.globalAlpha = 0.5; ctx.fillStyle = '#0f0'; ctx.fillRect(25, 25, 50, 40); diff --git a/dom/canvas/test/reftest/filters/reftest.list b/dom/canvas/test/reftest/filters/reftest.list index 9dd1d8c476..aba390d4b1 100644 --- a/dom/canvas/test/reftest/filters/reftest.list +++ b/dom/canvas/test/reftest/filters/reftest.list @@ -3,7 +3,7 @@ default-preferences pref(canvas.filters.enabled,true) == default-color.html ref.html == drop-shadow.html ref.html == drop-shadow-transformed.html ref.html -== global-alpha.html global-alpha-ref.html +fuzzy-if(azureSkia,1,1500) == global-alpha.html global-alpha-ref.html == global-composite-operation.html global-composite-operation-ref.html == liveness.html ref.html == multiple-drop-shadows.html shadow-ref.html diff --git a/dom/canvas/test/reftest/filters/units-em.html b/dom/canvas/test/reftest/filters/units-em.html index 1f280c1b5b..44f76dc4b4 100644 --- a/dom/canvas/test/reftest/filters/units-em.html +++ b/dom/canvas/test/reftest/filters/units-em.html @@ -10,8 +10,12 @@ var ctx = canvas.getContext('2d'); ctx.font = '20px sans-serif'; ctx.filter = 'drop-shadow(0 .5em black)'; ctx.fillStyle = '#0f0'; -ctx.fillRect(25, 25, 25, 40); +ctx.fillRect(25, 25, 50, 40); canvas.style.fontSize = '5px'; ctx.font = '4em sans-serif'; ctx.filter = 'drop-shadow(0 .5em black)'; + + + + diff --git a/dom/canvas/test/reftest/reftest.list b/dom/canvas/test/reftest/reftest.list index 6d2f299680..bb0e9cc03f 100644 --- a/dom/canvas/test/reftest/reftest.list +++ b/dom/canvas/test/reftest/reftest.list @@ -20,16 +20,15 @@ pref(webgl.force-layers-readback,true) == webgl-clear-test.html?readback wrappe == webgl-resize-test.html wrapper.html?green.png # Check that captureStream() displays in a local video element -skip-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-capturestream-test.html?preserve wrapper.html?green.png +== webgl-capturestream-test.html?preserve wrapper.html?green.png # Some of the failure conditions are a little crazy. I'm (jgilbert) setting these based on # failures encountered when running on Try, and then targetting the Try config by # differences in the `sandbox` contents. That is, I'm labeling based on symptoms rather # than cause. -# Lin-R-e10s: gtkWidget && browserIsRemote -# WinXP-R: winWidget && layersGPUAccelerated && !d2d -# Win7+-R: winWidget && layersGPUAccelerated && d2d -# Win7+-Ru: winWidget && !layersGPUAccelerated +# WinXP R: winWidget && layersGPUAccelerated && !d3d11 +# Win7+ R: winWidget && layersGPUAccelerated && d3d11 +# Win7+ Ru: winWidget && !layersGPUAccelerated && d3d11 # (Note that we have to remove spaces when used below) # IMPORTANT: Expected outcomes are evaluated left-to-right, and they replace eachother. @@ -42,73 +41,73 @@ skip-if(winWidget&&layersGPUAccelerated&&d2d) == webgl-capturestream-test.html?p # Does we draw the correct colors in the correct places? # Combinations: PowerSet([readback, aa, preserve, premult, alpha]) x [frame=1,frame=6] # This is 2^6 = 64 combinations. - == webgl-color-test.html?frame=1&__&________&_______&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=1&aa&________&_______&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=1&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=1&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=1&__&________&premult&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=1&aa&________&premult&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=1&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=1&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=1&__&________&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=1&aa&________&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=1&__&preserve&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=1&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png - == webgl-color-test.html?frame=1&__&________&premult&alpha wrapper.html?colors-premult.png - == webgl-color-test.html?frame=1&aa&________&premult&alpha wrapper.html?colors-premult.png - == webgl-color-test.html?frame=1&__&preserve&premult&alpha wrapper.html?colors-premult.png - == webgl-color-test.html?frame=1&aa&preserve&premult&alpha wrapper.html?colors-premult.png + == webgl-color-test.html?frame=1&__&________&_______&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=1&aa&________&_______&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=1&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=1&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=1&__&________&premult&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=1&aa&________&premult&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=1&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=1&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=1&__&________&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=1&aa&________&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=1&__&preserve&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=1&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png + == webgl-color-test.html?frame=1&__&________&premult&alpha wrapper.html?colors-premult.png + == webgl-color-test.html?frame=1&aa&________&premult&alpha wrapper.html?colors-premult.png + == webgl-color-test.html?frame=1&__&preserve&premult&alpha wrapper.html?colors-premult.png + == webgl-color-test.html?frame=1&aa&preserve&premult&alpha wrapper.html?colors-premult.png - == webgl-color-test.html?frame=6&__&________&_______&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=6&aa&________&_______&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=6&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=6&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=6&__&________&premult&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=6&aa&________&premult&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=6&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png - == webgl-color-test.html?frame=6&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=6&__&________&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=6&aa&________&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=6&__&preserve&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&(!layersGPUAccelerated||!d3d11)) == webgl-color-test.html?frame=6&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png - == webgl-color-test.html?frame=6&__&________&premult&alpha wrapper.html?colors-premult.png - == webgl-color-test.html?frame=6&aa&________&premult&alpha wrapper.html?colors-premult.png - == webgl-color-test.html?frame=6&__&preserve&premult&alpha wrapper.html?colors-premult.png - == webgl-color-test.html?frame=6&aa&preserve&premult&alpha wrapper.html?colors-premult.png + == webgl-color-test.html?frame=6&__&________&_______&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=6&aa&________&_______&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=6&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=6&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=6&__&________&premult&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=6&aa&________&premult&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=6&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png + == webgl-color-test.html?frame=6&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=6&__&________&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=6&aa&________&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=6&__&preserve&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) == webgl-color-test.html?frame=6&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png + == webgl-color-test.html?frame=6&__&________&premult&alpha wrapper.html?colors-premult.png + == webgl-color-test.html?frame=6&aa&________&premult&alpha wrapper.html?colors-premult.png + == webgl-color-test.html?frame=6&__&preserve&premult&alpha wrapper.html?colors-premult.png + == webgl-color-test.html?frame=6&aa&preserve&premult&alpha wrapper.html?colors-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&_______&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&_______&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&premult&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&premult&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&premult&alpha wrapper.html?colors-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&premult&alpha wrapper.html?colors-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&premult&alpha wrapper.html?colors-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&premult&alpha wrapper.html?colors-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&_______&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&_______&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&premult&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&premult&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&________&premult&alpha wrapper.html?colors-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&________&premult&alpha wrapper.html?colors-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&__&preserve&premult&alpha wrapper.html?colors-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=1&readback&aa&preserve&premult&alpha wrapper.html?colors-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&premult&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&premult&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&alpha wrapper.html?colors-non-premult.png -fuzzy(1,30000) fails-if(gtkWidget&&browserIsRemote) fails-if(winWidget&&(!layersGPUAccelerated||!d3d11)) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&premult&alpha wrapper.html?colors-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&premult&alpha wrapper.html?colors-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&premult&alpha wrapper.html?colors-premult.png - pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&alpha wrapper.html?colors-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&_______&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&premult&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&premult&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&premult&_____ wrapper.html?colors-no-alpha.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&_____ wrapper.html?colors-no-alpha.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&_______&alpha wrapper.html?colors-non-premult.png +fuzzy(1,30000) fails-if(winWidget&&layersGPUAccelerated&&!d3d11) pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&_______&alpha wrapper.html?colors-non-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&________&premult&alpha wrapper.html?colors-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&________&premult&alpha wrapper.html?colors-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&__&preserve&premult&alpha wrapper.html?colors-premult.png + pref(webgl.force-layers-readback,true) == webgl-color-test.html?frame=6&readback&aa&preserve&premult&alpha wrapper.html?colors-premult.png # Check for hanging bindings/state settings: == webgl-hanging-fb-test.html?__&________ wrapper.html?green.png @@ -153,10 +152,13 @@ skip-if(!winWidget) pref(webgl.disable-angle,true) == webgl-color-test.html?nat == stroketext-shadow.html stroketext-shadow-ref.html # focus rings -pref(canvas.focusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion<15,8,500) skip-if(winWidget) needs-focus == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html -pref(canvas.customfocusring.enabled,true) skip-if(B2G) skip-if(Android&&AndroidVersion<15,8,500) skip-if(winWidget) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html +pref(canvas.focusring.enabled,true) skip-if(B2G) skip-if(cocoaWidget) skip-if(winWidget) needs-focus == drawFocusIfNeeded.html drawFocusIfNeeded-ref.html +pref(canvas.customfocusring.enabled,true) skip-if(B2G) skip-if(cocoaWidget) skip-if(Android) skip-if(winWidget) fuzzy-if(gtkWidget,64,410) needs-focus == drawCustomFocusRing.html drawCustomFocusRing-ref.html # Check that captureStream() displays in a local video element -skip-if(winWidget&&layersGPUAccelerated&&d2d) == capturestream.html wrapper.html?green.png +== capturestream.html wrapper.html?green.png -fuzzy-if(Android,3,40) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html +fuzzy-if(azureSkia,16,2) fuzzy-if(Android,3,40) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),1,1) == 1177726-text-stroke-bounds.html 1177726-text-stroke-bounds-ref.html + +# Canvas Filter Reftests +include filters/reftest.list diff --git a/dom/canvas/test/test_canvas.html b/dom/canvas/test/test_canvas.html index 6333d23d5e..12df040b98 100644 --- a/dom/canvas/test/test_canvas.html +++ b/dom/canvas/test/test_canvas.html @@ -14366,11 +14366,7 @@ ctx.lineTo(50, 25); ctx.closePath(); ctx.stroke(); -if (IsAzureSkia()) { - isPixel(ctx, 50,25, 0,255,0,255, 0); -} else { - todo_isPixel(ctx, 50,25, 0,255,0,255, 0); -} +todo_isPixel(ctx, 50,25, 0,255,0,255, 0); } @@ -14507,11 +14503,7 @@ ctx.stroke(); ctx.strokeRect(50, 25, 0, 0); -if (IsAzureSkia()) { - isPixel(ctx, 50,25, 0,255,0,255, 0); -} else { - todo_isPixel(ctx, 50,25, 0,255,0,255, 0); -} +todo_isPixel(ctx, 50,25, 0,255,0,255, 0); } diff --git a/dom/contacts/tests/test_migration_chrome.js b/dom/contacts/tests/test_migration_chrome.js index 90b0d922ec..9aa2951ba5 100644 --- a/dom/contacts/tests/test_migration_chrome.js +++ b/dom/contacts/tests/test_migration_chrome.js @@ -5,9 +5,9 @@ */ "use strict"; -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; +var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -let imports = {}; +var imports = {}; Cu.import("resource://gre/modules/ContactDB.jsm", imports); Cu.import("resource://gre/modules/ContactService.jsm", imports); @@ -28,7 +28,7 @@ Object.defineProperty(this, "Promise", { value: imports.Promise, writable: false, configurable: false }); -let DEBUG = false; +var DEBUG = false; function debug(str) { if (DEBUG){ dump("-*- TestMigrationChromeScript: " + str + "\n"); diff --git a/dom/devicestorage/test/chrome.ini b/dom/devicestorage/test/chrome.ini index bedf4638bb..d251a742d9 100644 --- a/dom/devicestorage/test/chrome.ini +++ b/dom/devicestorage/test/chrome.ini @@ -1,5 +1,5 @@ [DEFAULT] -skip-if = (buildapp == 'b2g' || buildapp == 'mulet') +skip-if = (buildapp == 'b2g' || os == 'android') [test_app_permissions.html] [test_fs_app_permissions.html] diff --git a/dom/devicestorage/test/mochitest.ini b/dom/devicestorage/test/mochitest.ini index cee91b441d..f4cd8ab9bf 100644 --- a/dom/devicestorage/test/mochitest.ini +++ b/dom/devicestorage/test/mochitest.ini @@ -1,5 +1,4 @@ [DEFAULT] -skip-if = (toolkit == 'android' && processor == 'x86') # Android: bug 781789 & bug 782275 support-files = devicestorage_common.js remove_testing_directory.js @@ -10,7 +9,6 @@ support-files = devicestorage_common.js [test_available.html] [test_basic.html] [test_dirs.html] -skip-if = e10s # Bug 1063569. # [test_diskSpace.html] # Possible race between the time we write a file, and the # time it takes to be reflected by statfs(). Bug # 791287 diff --git a/dom/devicestorage/test/test_fs_remove.html b/dom/devicestorage/test/test_fs_remove.html index 29b50c2b76..78cabef4cf 100644 --- a/dom/devicestorage/test/test_fs_remove.html +++ b/dom/devicestorage/test/test_fs_remove.html @@ -161,7 +161,7 @@ function testNextRemove() { ok(navigator.getDeviceStorage, "Should have getDeviceStorage."); -let gStorage = navigator.getDeviceStorage("pictures"); +gStorage = navigator.getDeviceStorage("pictures"); ok(gStorage, "Should have gotten a storage."); // Test "removeDeep" first. diff --git a/dom/encoding/test/reftest/reftest.list b/dom/encoding/test/reftest/reftest.list index 43558be042..ec25c70a57 100644 --- a/dom/encoding/test/reftest/reftest.list +++ b/dom/encoding/test/reftest/reftest.list @@ -1,5 +1,5 @@ == bug863728-1.html bug863728-1-ref.html -== bug863728-2.html bug863728-2-ref.html +fuzzy-if(skiaContent,1,10) == bug863728-2.html bug863728-2-ref.html == bug863728-3.html bug863728-3-ref.html == bug945215-1.html bug945215-1-ref.html -== bug945215-2.html bug945215-2-ref.html +fuzzy-if(skiaContent,1,10) == bug945215-2.html bug945215-2-ref.html diff --git a/dom/encoding/test/unit/head.js b/dom/encoding/test/unit/head.js index 8094737c23..116824509e 100644 --- a/dom/encoding/test/unit/head.js +++ b/dom/encoding/test/unit/head.js @@ -79,7 +79,7 @@ function assert_throws(ex, func) { Components.stack.caller, false); } -let tests = []; +var tests = []; function test(func, msg) { tests.push({msg: msg, func: func, diff --git a/dom/events/crashtests/938341.html b/dom/events/crashtests/938341.html new file mode 100644 index 0000000000..3190b4a6b5 --- /dev/null +++ b/dom/events/crashtests/938341.html @@ -0,0 +1,7 @@ + + + +
\ No newline at end of file diff --git a/dom/events/crashtests/crashtests.list b/dom/events/crashtests/crashtests.list index 88948dfdba..fb11e4ec53 100644 --- a/dom/events/crashtests/crashtests.list +++ b/dom/events/crashtests/crashtests.list @@ -5,6 +5,7 @@ load 422009-1.xhtml load 457776-1.html load 496308-1.html load 682637-1.html +load 938341.html load 1033343.html load 1035654-1.html load 1035654-2.html diff --git a/dom/events/test/bug418986-3.js b/dom/events/test/bug418986-3.js index 317b5c7ad1..6bd2a6e693 100644 --- a/dom/events/test/bug418986-3.js +++ b/dom/events/test/bug418986-3.js @@ -1,7 +1,7 @@ SimpleTest.waitForExplicitFinish(); // The main testing function. -let test = function (isContent) { +var test = function (isContent) { // Each definition is [eventType, prefSetting] // Where we are setting the "privacy.resistFingerprinting" pref. let eventDefs = [["mousedown", true], diff --git a/dom/html/test/browser_bug1081537.js b/dom/html/test/browser_bug1081537.js index 67cd9904d3..8ae53e4fff 100644 --- a/dom/html/test/browser_bug1081537.js +++ b/dom/html/test/browser_bug1081537.js @@ -1,6 +1,6 @@ // This test is useful because mochitest-browser runs as an addon, so we test // addon-scope paths here. -let ifr; +var ifr; function test() { ifr = document.createElement('iframe'); document.getElementById('main-window').appendChild(ifr); diff --git a/dom/html/test/browser_bug649778.js b/dom/html/test/browser_bug649778.js index 80d966741e..6356d20fe5 100644 --- a/dom/html/test/browser_bug649778.js +++ b/dom/html/test/browser_bug649778.js @@ -4,8 +4,8 @@ var testPath = "http://mochi.test:8888/browser/dom/html/test/"; var popup; -let {LoadContextInfo} = Cu.import("resource://gre/modules/LoadContextInfo.jsm", null); -let {Services} = Cu.import("resource://gre/modules/Services.jsm", null); +var {LoadContextInfo} = Cu.import("resource://gre/modules/LoadContextInfo.jsm", null); +var {Services} = Cu.import("resource://gre/modules/Services.jsm", null); function checkCache(url, inMemory, shouldExist, cb) { diff --git a/dom/html/test/formSubmission_chrome.js b/dom/html/test/formSubmission_chrome.js index 97fd4f18c2..9943bb1d94 100644 --- a/dom/html/test/formSubmission_chrome.js +++ b/dom/html/test/formSubmission_chrome.js @@ -1,4 +1,4 @@ -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.importGlobalProperties(["File"]); addMessageListener("files.open", function (message) { diff --git a/dom/indexedDB/test/browserHelpers.js b/dom/indexedDB/test/browserHelpers.js index b1bc32ffaf..190777ea21 100644 --- a/dom/indexedDB/test/browserHelpers.js +++ b/dom/indexedDB/test/browserHelpers.js @@ -3,10 +3,10 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -let testGenerator = testSteps(); +var testGenerator = testSteps(); -let testResult; -let testException; +var testResult; +var testException; function testFinishedCallback(result, exception) { diff --git a/dom/indexedDB/test/browser_bug839193.js b/dom/indexedDB/test/browser_bug839193.js index 3e982d7d63..eef284794b 100644 --- a/dom/indexedDB/test/browser_bug839193.js +++ b/dom/indexedDB/test/browser_bug839193.js @@ -2,9 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -let gTestRoot = getRootDirectory(gTestPath); -let gBugWindow = null; -let gIterations = 5; +var gTestRoot = getRootDirectory(gTestPath); +var gBugWindow = null; +var gIterations = 5; function onLoad() { gBugWindow.close(); diff --git a/dom/indexedDB/test/chromeHelpers.js b/dom/indexedDB/test/chromeHelpers.js index de4213f624..16508f62a4 100644 --- a/dom/indexedDB/test/chromeHelpers.js +++ b/dom/indexedDB/test/chromeHelpers.js @@ -3,9 +3,9 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; +var { 'classes': Cc, 'interfaces': Ci, 'utils': Cu } = Components; -let testGenerator = testSteps(); +var testGenerator = testSteps(); if (!window.runTest) { window.runTest = function() diff --git a/dom/indexedDB/test/head.js b/dom/indexedDB/test/head.js index 7b58ef7687..785cfba90e 100644 --- a/dom/indexedDB/test/head.js +++ b/dom/indexedDB/test/head.js @@ -3,7 +3,7 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ -let gActiveListeners = {}; +var gActiveListeners = {}; function registerPopupEventHandler(eventName, callback) { gActiveListeners[eventName] = function (event) { diff --git a/dom/interfaces/push/nsIPushErrorReporter.idl b/dom/interfaces/push/nsIPushErrorReporter.idl index b010258121..d9cef82ea6 100644 --- a/dom/interfaces/push/nsIPushErrorReporter.idl +++ b/dom/interfaces/push/nsIPushErrorReporter.idl @@ -9,8 +9,7 @@ interface nsIPushErrorReporter : nsISupports { /** - * Ack status codes, reported when the Push service acknowledges an incoming - * message. + * Ack types, reported when the Push service acknowledges an incoming message. * * Acks are sent before the message is dispatched to the service worker, * since the server delays new messages until all outstanding ones have been @@ -39,7 +38,7 @@ interface nsIPushErrorReporter : nsISupports /** * Reports a `push` event handler error to the Push service. |messageId| is * an opaque string passed to `nsIPushNotifier.notifyPush{WithData}`. - * |status| is a delivery error reason. + * |reason| is a delivery error reason. */ void reportDeliveryError(in DOMString messageId, [optional] in uint16_t reason); diff --git a/dom/interfaces/push/nsIPushNotifier.idl b/dom/interfaces/push/nsIPushNotifier.idl index 8139e3897f..ce17c7d5ea 100644 --- a/dom/interfaces/push/nsIPushNotifier.idl +++ b/dom/interfaces/push/nsIPushNotifier.idl @@ -5,23 +5,53 @@ #include "nsISupports.idl" +%{C++ +#include "nsTArray.h" +#include "mozilla/Maybe.h" + +#define PUSHNOTIFIER_CONTRACTID \ + "@mozilla.org/push/Notifier;1" + +// These constants are duplicated in `PushComponents.js`. +#define OBSERVER_TOPIC_PUSH "push-message" +#define OBSERVER_TOPIC_SUBSCRIPTION_CHANGE "push-subscription-change" +#define OBSERVER_TOPIC_SUBSCRIPTION_LOST "push-subscription-lost" +%} + interface nsIPrincipal; +[ref] native MaybeData(const mozilla::Maybe>); + /** - * Fires service worker events for push messages sent to content subscriptions, - * and XPCOM observer notifications for system subscriptions. + * Fires XPCOM observer notifications and service worker events for + * messages sent to push subscriptions. */ [scriptable, builtinclass, uuid(b00dfdeb-14e5-425b-adc7-b531442e3216)] interface nsIPushNotifier : nsISupports { + /** + * Fires a `push-message` observer notification, and sends a `push` event to + * the service worker registered for the |scope|. |messageId| is an opaque ID + * used to report errors if the worker fails to handle the message. + */ void notifyPush(in ACString scope, in nsIPrincipal principal, in DOMString messageId); + /** + * Same as `notifyPush`, except the subject of the observer notification + * receives an `nsIPushMessage` instance containing the |data|. Service + * workers can access the |data| via the `PushMessageData` WebIDL interface. + */ void notifyPushWithData(in ACString scope, in nsIPrincipal principal, in DOMString messageId, [optional] in uint32_t dataLen, [array, size_is(dataLen)] in uint8_t data); + /** + * Fires a `push-subscription-change` observer notification, and sends a + * `pushsubscriptionchange` event to the service worker registered for the + * |scope|. + */ void notifySubscriptionChange(in ACString scope, in nsIPrincipal principal); /** @@ -41,12 +71,39 @@ interface nsIPushNotifier : nsISupports void notifyError(in ACString scope, in nsIPrincipal principal, in DOMString message, in uint32_t flags); + + /** + * Internal methods used to fire service worker events and observer + * notifications. These are not exposed to JavaScript. + */ + + [noscript, nostdcall] + void notifyPushWorkers(in ACString scope, + in nsIPrincipal principal, + in DOMString messageId, + in MaybeData data); + + [noscript, nostdcall] + void notifyPushObservers(in ACString scope, in MaybeData data); + + [noscript, nostdcall] + void notifySubscriptionChangeWorkers(in ACString scope, + in nsIPrincipal principal); + + [noscript, nostdcall] + void notifySubscriptionChangeObservers(in ACString scope); + + [noscript, nostdcall] + void notifySubscriptionLostObservers(in ACString scope, in uint16_t reason); + + [noscript, nostdcall, notxpcom] + void notifyErrorWorkers(in ACString scope, in DOMString message, + in uint32_t flags); }; /** - * A push message sent to a system subscription, used as the subject of a - * `push-message` observer notification. System subscriptions are created by - * the system principal, and do not use worker events. + * A push message sent to a subscription, used as the subject of a + * `push-message` observer notification. * * This interface resembles the `PushMessageData` WebIDL interface. */ diff --git a/dom/interfaces/push/nsIPushService.idl b/dom/interfaces/push/nsIPushService.idl index 27683bf8e0..29e40fc6a8 100644 --- a/dom/interfaces/push/nsIPushService.idl +++ b/dom/interfaces/push/nsIPushService.idl @@ -18,6 +18,7 @@ interface nsIPushSubscription : nsISupports readonly attribute long long pushCount; readonly attribute long long lastPush; readonly attribute long quota; + readonly attribute bool isSystemSubscription; bool quotaApplies(); bool isExpired(); diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index e6b7b92930..a31e1c3328 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -166,7 +166,7 @@ #endif #ifndef MOZ_SIMPLEPUSH -#include "mozilla/dom/PushNotifier.h" +#include "nsIPushNotifier.h" #endif #include "mozilla/dom/File.h" @@ -3196,13 +3196,11 @@ ContentChild::RecvPush(const nsCString& aScope, const nsString& aMessageId) { #ifndef MOZ_SIMPLEPUSH - nsCOMPtr pushNotifierIface = + nsCOMPtr pushNotifier = do_GetService("@mozilla.org/push/Notifier;1"); - if (NS_WARN_IF(!pushNotifierIface)) { + if (NS_WARN_IF(!pushNotifier)) { return true; } - PushNotifier* pushNotifier = - static_cast(pushNotifierIface.get()); nsresult rv = pushNotifier->NotifyPushObservers(aScope, Nothing()); Unused << NS_WARN_IF(NS_FAILED(rv)); @@ -3221,13 +3219,11 @@ ContentChild::RecvPushWithData(const nsCString& aScope, InfallibleTArray&& aData) { #ifndef MOZ_SIMPLEPUSH - nsCOMPtr pushNotifierIface = + nsCOMPtr pushNotifier = do_GetService("@mozilla.org/push/Notifier;1"); - if (NS_WARN_IF(!pushNotifierIface)) { + if (NS_WARN_IF(!pushNotifier)) { return true; } - PushNotifier* pushNotifier = - static_cast(pushNotifierIface.get()); nsresult rv = pushNotifier->NotifyPushObservers(aScope, Some(aData)); Unused << NS_WARN_IF(NS_FAILED(rv)); @@ -3244,13 +3240,11 @@ ContentChild::RecvPushSubscriptionChange(const nsCString& aScope, const IPC::Principal& aPrincipal) { #ifndef MOZ_SIMPLEPUSH - nsCOMPtr pushNotifierIface = + nsCOMPtr pushNotifier = do_GetService("@mozilla.org/push/Notifier;1"); - if (NS_WARN_IF(!pushNotifierIface)) { + if (NS_WARN_IF(!pushNotifier)) { return true; } - PushNotifier* pushNotifier = - static_cast(pushNotifierIface.get()); nsresult rv = pushNotifier->NotifySubscriptionChangeObservers(aScope); Unused << NS_WARN_IF(NS_FAILED(rv)); @@ -3266,13 +3260,11 @@ ContentChild::RecvPushError(const nsCString& aScope, const nsString& aMessage, const uint32_t& aFlags) { #ifndef MOZ_SIMPLEPUSH - nsCOMPtr pushNotifierIface = + nsCOMPtr pushNotifier = do_GetService("@mozilla.org/push/Notifier;1"); - if (NS_WARN_IF(!pushNotifierIface)) { + if (NS_WARN_IF(!pushNotifier)) { return true; } - PushNotifier* pushNotifier = - static_cast(pushNotifierIface.get()); pushNotifier->NotifyErrorWorkers(aScope, aMessage, aFlags); #endif return true; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 54f48b9cd0..9dba21f430 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -260,7 +260,7 @@ using namespace mozilla::system; #endif #ifndef MOZ_SIMPLEPUSH -#include "mozilla/dom/PushNotifier.h" +#include "nsIPushNotifier.h" #endif #ifdef XP_WIN @@ -5832,13 +5832,11 @@ ContentParent::RecvNotifyPushObservers(const nsCString& aScope, const nsString& aMessageId) { #ifndef MOZ_SIMPLEPUSH - nsCOMPtr pushNotifierIface = + nsCOMPtr pushNotifier = do_GetService("@mozilla.org/push/Notifier;1"); - if (NS_WARN_IF(!pushNotifierIface)) { + if (NS_WARN_IF(!pushNotifier)) { return true; } - PushNotifier* pushNotifier = - static_cast(pushNotifierIface.get()); nsresult rv = pushNotifier->NotifyPushObservers(aScope, Nothing()); Unused << NS_WARN_IF(NS_FAILED(rv)); @@ -5852,13 +5850,11 @@ ContentParent::RecvNotifyPushObserversWithData(const nsCString& aScope, InfallibleTArray&& aData) { #ifndef MOZ_SIMPLEPUSH - nsCOMPtr pushNotifierIface = + nsCOMPtr pushNotifier = do_GetService("@mozilla.org/push/Notifier;1"); - if (NS_WARN_IF(!pushNotifierIface)) { + if (NS_WARN_IF(!pushNotifier)) { return true; } - PushNotifier* pushNotifier = - static_cast(pushNotifierIface.get()); nsresult rv = pushNotifier->NotifyPushObservers(aScope, Some(aData)); Unused << NS_WARN_IF(NS_FAILED(rv)); @@ -5870,13 +5866,11 @@ bool ContentParent::RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope) { #ifndef MOZ_SIMPLEPUSH - nsCOMPtr pushNotifierIface = + nsCOMPtr pushNotifier = do_GetService("@mozilla.org/push/Notifier;1"); - if (NS_WARN_IF(!pushNotifierIface)) { + if (NS_WARN_IF(!pushNotifier)) { return true; } - PushNotifier* pushNotifier = - static_cast(pushNotifierIface.get()); nsresult rv = pushNotifier->NotifySubscriptionChangeObservers(aScope); Unused << NS_WARN_IF(NS_FAILED(rv)); @@ -5889,13 +5883,11 @@ ContentParent::RecvNotifyPushSubscriptionLostObservers(const nsCString& aScope, const uint16_t& aReason) { #ifndef MOZ_SIMPLEPUSH - nsCOMPtr pushNotifierIface = + nsCOMPtr pushNotifier = do_GetService("@mozilla.org/push/Notifier;1"); - if (NS_WARN_IF(!pushNotifierIface)) { + if (NS_WARN_IF(!pushNotifier)) { return true; } - PushNotifier* pushNotifier = - static_cast(pushNotifierIface.get()); nsresult rv = pushNotifier->NotifySubscriptionLostObservers(aScope, aReason); Unused << NS_WARN_IF(NS_FAILED(rv)); diff --git a/dom/locales/en-US/chrome/security/csp.properties b/dom/locales/en-US/chrome/security/csp.properties index 99f7937d89..a283a37d5a 100644 --- a/dom/locales/en-US/chrome/security/csp.properties +++ b/dom/locales/en-US/chrome/security/csp.properties @@ -35,10 +35,10 @@ ignoringDuplicateSrc = Ignoring duplicate source %1$S # LOCALIZATION NOTE (ignoringSrcFromMetaCSP): # %1$S defines the ignored src ignoringSrcFromMetaCSP = Ignoring source '%1$S' (Not supported when delivered via meta element). -# LOCALIZATION NOTE (ignoringSrcWithinScriptSrc): +# LOCALIZATION NOTE (ignoringSrcWithinScriptStyleSrc): # %1$S is the ignored src -# script-src is a directive name and should not be localized -ignoringSrcWithinScriptSrc = Ignoring "%1$S" within script-src: nonce-source or hash-source specified +# script-src and style-src are directive names and should not be localized +ignoringSrcWithinScriptStyleSrc = Ignoring "%1$S" within script-src or style-src: nonce-source or hash-source specified # LOCALIZATION NOTE (reportURInotHttpsOrHttp2): # %1$S is the ETLD of the report URI that is not HTTP or HTTPS reportURInotHttpsOrHttp2 = The report URI (%1$S) should be an HTTP or HTTPS URI. diff --git a/dom/manifest/test/mochitest.ini b/dom/manifest/test/mochitest.ini index c3c841c0b2..a546c0a9ac 100644 --- a/dom/manifest/test/mochitest.ini +++ b/dom/manifest/test/mochitest.ini @@ -1,5 +1,4 @@ [DEFAULT] - support-files = common.js resource.sjs @@ -7,6 +6,7 @@ support-files = [test_ImageObjectProcessor_sizes.html] [test_ImageObjectProcessor_src.html] [test_ImageObjectProcessor_type.html] +[test_ManifestProcessor_background_color.html] [test_ManifestProcessor_dir.html] [test_ManifestProcessor_display.html] [test_ManifestProcessor_icons.html] @@ -16,3 +16,5 @@ support-files = [test_ManifestProcessor_orientation.html] [test_ManifestProcessor_scope.html] [test_ManifestProcessor_start_url.html] +[test_ManifestProcessor_theme_color.html] +[test_ManifestProcessor_warnings.html] diff --git a/dom/manifest/test/test_ImageObjectProcessor_src.html b/dom/manifest/test/test_ImageObjectProcessor_src.html index 78c122393f..cb77af0bdd 100644 --- a/dom/manifest/test/test_ImageObjectProcessor_src.html +++ b/dom/manifest/test/test_ImageObjectProcessor_src.html @@ -35,7 +35,7 @@ var noSrc = { var expected = `Expect icons without a src prop to be filtered out.`; data.jsonText = JSON.stringify(noSrc); var result = processor.process(data); -ise(result.icons.length, 0, expected); +is(result.icons.length, 0, expected); var invalidSrc = { icons: [{ @@ -56,7 +56,7 @@ var invalidSrc = { var expected = `Expect icons with invalid src prop to be filtered out.`; data.jsonText = JSON.stringify(noSrc); var result = processor.process(data); -ise(result.icons.length, 0, expected); +is(result.icons.length, 0, expected); var expected = `Expect icon's src to be a string.`; var withSrc = { @@ -66,7 +66,7 @@ var withSrc = { }; data.jsonText = JSON.stringify(withSrc); var result = processor.process(data); -ise(typeof result.icons[0].src, "string", expected); +is(typeof result.icons[0].src, "string", expected); var expected = `Expect only icons with a src prop to be kept.`; var withSrc = { @@ -80,12 +80,12 @@ var withSrc = { }; data.jsonText = JSON.stringify(withSrc); var result = processor.process(data); -ise(result.icons.length, 2, expected); +is(result.icons.length, 2, expected); var expectedURL = new URL('pass', manifestURL); for (var icon of result.icons) { var expected = `Expect src prop to be ${expectedURL.toString()}`; - ise(icon.src.toString(), expectedURL.toString(), expected); + is(icon.src.toString(), expectedURL.toString(), expected); } //Resolve URLs relative to manfiest @@ -100,7 +100,7 @@ URLs.forEach((url) => { }); var absURL = new URL(url, manifestURL).toString(); var result = processor.process(data); - ise(result.icons[0].src.toString(), absURL, expected); + is(result.icons[0].src.toString(), absURL, expected); }); diff --git a/dom/manifest/test/test_ImageObjectProcessor_type.html b/dom/manifest/test/test_ImageObjectProcessor_type.html index 17e74a3f52..d1b95044de 100644 --- a/dom/manifest/test/test_ImageObjectProcessor_type.html +++ b/dom/manifest/test/test_ImageObjectProcessor_type.html @@ -35,7 +35,7 @@ invalidMimeTypes.forEach((invalidMime) => { testIcon.icons[0].type = invalidMime; data.jsonText = JSON.stringify(testIcon); var result = processor.process(data); - ise(result.icons[0].type, undefined, expected); + is(result.icons[0].type, undefined, expected); }); var validTypes = [ @@ -51,7 +51,7 @@ validTypes.forEach((validMime) => { testIcon.icons[0].type = validMime; data.jsonText = JSON.stringify(testIcon); var result = processor.process(data); - ise(result.icons[0].type, 'image/jpeg', expected); + is(result.icons[0].type, 'image/jpeg', expected); }); diff --git a/dom/manifest/test/test_ManifestProcessor_background_color.html b/dom/manifest/test/test_ManifestProcessor_background_color.html new file mode 100644 index 0000000000..c3fbdae347 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_background_color.html @@ -0,0 +1,94 @@ + + + + + + Test for Bug 1195018 + + + + + diff --git a/dom/manifest/test/test_ManifestProcessor_theme_color.html b/dom/manifest/test/test_ManifestProcessor_theme_color.html new file mode 100644 index 0000000000..6f27c9d0c4 --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_theme_color.html @@ -0,0 +1,94 @@ + + + + + + Test for Bug 1195018 + + + + + diff --git a/dom/manifest/test/test_ManifestProcessor_warnings.html b/dom/manifest/test/test_ManifestProcessor_warnings.html new file mode 100644 index 0000000000..865ef8054a --- /dev/null +++ b/dom/manifest/test/test_ManifestProcessor_warnings.html @@ -0,0 +1,90 @@ + + + + + + Test for Bug 1086997 + + + + + diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index bde842f7e5..9ce80a7afb 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -303,14 +303,21 @@ MediaFormatReader::OnDemuxerInitDone(nsresult) mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); return; } - mInfo.mAudio = *mAudio.mTrackDemuxer->GetInfo()->GetAsAudioInfo(); - UniquePtr info(mAudio.mTrackDemuxer->GetInfo()); - for (const MetadataTag& tag : info->mTags) { - tags->Put(tag.mKey, tag.mValue); + UniquePtr audioInfo = mAudio.mTrackDemuxer->GetInfo(); + // We actively ignore audio tracks that we know we can't play. + audioActive = audioInfo && audioInfo->IsValid(); + if (audioActive) { + mInfo.mAudio = *audioInfo->GetAsAudioInfo(); + for (const MetadataTag& tag : audioInfo->mTags) { + tags->Put(tag.mKey, tag.mValue); + } + mAudio.mCallback = new DecoderCallback(this, TrackInfo::kAudioTrack); + mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered(); + mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock(); + } else { + mAudio.mTrackDemuxer->BreakCycles(); + mAudio.mTrackDemuxer = nullptr; } - mAudio.mCallback = new DecoderCallback(this, TrackInfo::kAudioTrack); - mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered(); - mTrackDemuxersMayBlock |= mAudio.mTrackDemuxer->GetSamplesMayBlock(); } UniquePtr crypto = mDemuxer->GetCrypto(); diff --git a/dom/media/MediaInfo.h b/dom/media/MediaInfo.h index a1ebd58fa4..b76f1050b7 100644 --- a/dom/media/MediaInfo.h +++ b/dom/media/MediaInfo.h @@ -34,6 +34,9 @@ public: nsCString mValue; }; + // Maximum channel number we can currently handle (7.1) +#define MAX_AUDIO_CHANNELS 8 + class TrackInfo { public: enum TrackType { @@ -331,7 +334,7 @@ public: bool IsValid() const override { - return mChannels > 0 && mRate > 0; + return mChannels > 0 && mChannels <= MAX_AUDIO_CHANNELS && mRate > 0; } AudioInfo* GetAsAudioInfo() override @@ -543,9 +546,6 @@ public: const nsCString& mMimeType; }; -// Maximum channel number we can currently handle (7.1) -#define MAX_AUDIO_CHANNELS 8 - class AudioConfig { public: enum Channel { @@ -685,6 +685,11 @@ public: return !(*this == aOther); } + bool IsValid() const + { + return mChannelLayout.IsValid() && Format() != FORMAT_NONE && Rate() > 0; + } + static const char* FormatToString(SampleFormat aFormat); static uint32_t SampleSize(SampleFormat aFormat); static uint32_t FormatToBits(SampleFormat aFormat); diff --git a/dom/media/gmp/GMPServiceChild.cpp b/dom/media/gmp/GMPServiceChild.cpp index 9c1f941ad2..0569a67418 100644 --- a/dom/media/gmp/GMPServiceChild.cpp +++ b/dom/media/gmp/GMPServiceChild.cpp @@ -72,10 +72,11 @@ public: base::ProcessId otherProcess; nsCString displayName; uint32_t pluginId; + nsresult rv; bool ok = aGMPServiceChild->SendLoadGMP(mNodeId, mAPI, mTags, alreadyBridgedTo, &otherProcess, - &displayName, &pluginId); - if (!ok) { + &displayName, &pluginId, &rv); + if (!ok || rv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) { mCallback->Done(nullptr); return; } diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp index ac8b60ce8f..b9e75fb78f 100644 --- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -685,6 +685,13 @@ GeckoMediaPluginServiceParent::NotifySyncShutdownComplete() mWaitingForPluginsSyncShutdown = false; } +bool +GeckoMediaPluginServiceParent::IsShuttingDown() +{ + MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); + return mShuttingDownOnGMPThread; +} + void GeckoMediaPluginServiceParent::UnloadPlugins() { @@ -1785,8 +1792,15 @@ GMPServiceParent::RecvLoadGMP(const nsCString& aNodeId, nsTArray&& aAlreadyBridgedTo, ProcessId* aId, nsCString* aDisplayName, - uint32_t* aPluginId) + uint32_t* aPluginId, + nsresult* aRv) { + *aRv = NS_OK; + if (mService->IsShuttingDown()) { + *aRv = NS_ERROR_ILLEGAL_DURING_SHUTDOWN; + return true; + } + RefPtr gmp = mService->SelectPluginForAPI(aNodeId, aAPI, aTags); nsCString api = aTags[0]; diff --git a/dom/media/gmp/GMPServiceParent.h b/dom/media/gmp/GMPServiceParent.h index 5859f0ddb0..3a5d0987a1 100644 --- a/dom/media/gmp/GMPServiceParent.h +++ b/dom/media/gmp/GMPServiceParent.h @@ -59,6 +59,9 @@ public: RefPtr EnsureInitialized(); RefPtr AsyncAddPluginDirectory(const nsAString& aDirectory); + // GMP thread access only + bool IsShuttingDown(); + private: friend class GMPServiceParent; @@ -221,7 +224,8 @@ public: nsTArray&& aAlreadyBridgedTo, base::ProcessId* aID, nsCString* aDisplayName, - uint32_t* aPluginId) override; + uint32_t* aPluginId, + nsresult* aRv) override; bool RecvGetGMPNodeId(const nsString& aOrigin, const nsString& aTopLevelOrigin, const nsString& aGMPName, diff --git a/dom/media/gmp/GMPVideoDecoderProxy.h b/dom/media/gmp/GMPVideoDecoderProxy.h index 6043f5cd25..b9596fd21b 100644 --- a/dom/media/gmp/GMPVideoDecoderProxy.h +++ b/dom/media/gmp/GMPVideoDecoderProxy.h @@ -14,8 +14,6 @@ #include "GMPCallbackBase.h" #include "GMPUtils.h" -class nsCString; - class GMPVideoDecoderCallbackProxy : public GMPCallbackBase, public GMPVideoDecoderCallback { diff --git a/dom/media/gmp/PGMPService.ipdl b/dom/media/gmp/PGMPService.ipdl index 7eba30cbc1..672f859003 100644 --- a/dom/media/gmp/PGMPService.ipdl +++ b/dom/media/gmp/PGMPService.ipdl @@ -17,7 +17,8 @@ sync protocol PGMPService parent: sync LoadGMP(nsCString nodeId, nsCString api, nsCString[] tags, ProcessId[] alreadyBridgedTo) - returns (ProcessId id, nsCString displayName, uint32_t pluginId); + returns (ProcessId id, nsCString displayName, uint32_t pluginId, + nsresult aResult); sync GetGMPNodeId(nsString origin, nsString topLevelOrigin, nsString gmpName, bool inPrivateBrowsing) diff --git a/dom/media/gtest/TestRust.cpp b/dom/media/gtest/TestRust.cpp new file mode 100644 index 0000000000..7c989d29da --- /dev/null +++ b/dom/media/gtest/TestRust.cpp @@ -0,0 +1,8 @@ +#include + +extern "C" uint8_t* test_rust(); + +TEST(rust, CallFromCpp) { + auto greeting = test_rust(); + EXPECT_STREQ(reinterpret_cast(greeting), "hello from rust."); +} diff --git a/dom/media/gtest/hello.rs b/dom/media/gtest/hello.rs new file mode 100644 index 0000000000..cd111882ae --- /dev/null +++ b/dom/media/gtest/hello.rs @@ -0,0 +1,6 @@ +#[no_mangle] +pub extern fn test_rust() -> *const u8 { + // NB: rust &str aren't null terminated. + let greeting = "hello from rust.\0"; + greeting.as_ptr() +} diff --git a/dom/media/gtest/moz.build b/dom/media/gtest/moz.build index d54c3e6e4b..9d5625bbd7 100644 --- a/dom/media/gtest/moz.build +++ b/dom/media/gtest/moz.build @@ -29,6 +29,11 @@ if CONFIG['MOZ_WEBM_ENCODER']: 'TestWebMWriter.cpp', ] +if CONFIG['MOZ_RUST']: + SOURCES += ['hello.rs',] + UNIFIED_SOURCES += ['TestRust.cpp',] + + TEST_HARNESS_FILES.gtest += [ '../test/gizmo-frag.mp4', '../test/gizmo.mp4', diff --git a/dom/media/mediasource/MediaSourceDecoder.cpp b/dom/media/mediasource/MediaSourceDecoder.cpp index 54b04f4e03..aaf436fd1b 100644 --- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -283,7 +283,7 @@ MediaSourceDecoder::NextFrameBufferedStatus() TimeInterval interval(currentPosition, currentPosition + media::TimeUnit::FromMicroseconds(DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED), MediaSourceDemuxer::EOS_FUZZ); - return GetBuffered().Contains(interval) + return GetBuffered().Contains(ClampIntervalToEnd(interval)) ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE; } @@ -313,7 +313,17 @@ MediaSourceDecoder::CanPlayThrough() TimeInterval interval(currentPosition, timeAhead, MediaSourceDemuxer::EOS_FUZZ); - return GetBuffered().Contains(interval); + return GetBuffered().Contains(ClampIntervalToEnd(interval)); +} + +TimeInterval +MediaSourceDecoder::ClampIntervalToEnd(const TimeInterval& aInterval) +{ + if (!mEnded) { + return aInterval; + } + TimeInterval interval(TimeUnit(), TimeUnit::FromSeconds(GetDuration())); + return aInterval.Intersection(interval); } #undef MSE_DEBUG diff --git a/dom/media/mediasource/MediaSourceDecoder.h b/dom/media/mediasource/MediaSourceDecoder.h index ff3110eda3..3ac0e20c04 100644 --- a/dom/media/mediasource/MediaSourceDecoder.h +++ b/dom/media/mediasource/MediaSourceDecoder.h @@ -83,6 +83,7 @@ public: private: void DoSetMediaSourceDuration(double aDuration); + media::TimeInterval ClampIntervalToEnd(const media::TimeInterval& aInterval); // The owning MediaSource holds a strong reference to this decoder, and // calls Attach/DetachMediaSource on this decoder to set and clear diff --git a/dom/media/mediasource/gtest/moz.build b/dom/media/mediasource/gtest/moz.build index 787c84fb46..0b2b5b6e3a 100644 --- a/dom/media/mediasource/gtest/moz.build +++ b/dom/media/mediasource/gtest/moz.build @@ -14,5 +14,3 @@ LOCAL_INCLUDES += [ ] FINAL_LIBRARY = 'xul-gtest' - -FAIL_ON_WARNINGS = True diff --git a/dom/media/mediasource/moz.build b/dom/media/mediasource/moz.build index 2872580060..ef047e705e 100644 --- a/dom/media/mediasource/moz.build +++ b/dom/media/mediasource/moz.build @@ -38,4 +38,7 @@ TEST_DIRS += [ 'gtest', ] +if CONFIG['MOZ_GONK_MEDIACODEC']: + DEFINES['MOZ_GONK_MEDIACODEC'] = True + FINAL_LIBRARY = 'xul' diff --git a/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video1.m4s b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video1.m4s new file mode 100644 index 0000000000..3dad336e8e Binary files /dev/null and b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video1.m4s differ diff --git a/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video1.m4s^headers^ b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video1.m4s^headers^ new file mode 100644 index 0000000000..4030ea1d3d --- /dev/null +++ b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video1.m4s^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video2.m4s b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video2.m4s new file mode 100644 index 0000000000..dd7491241f Binary files /dev/null and b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video2.m4s differ diff --git a/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video2.m4s^headers^ b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video2.m4s^headers^ new file mode 100644 index 0000000000..4030ea1d3d --- /dev/null +++ b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-video2.m4s^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-videoinit.mp4 b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-videoinit.mp4 new file mode 100644 index 0000000000..b1a2d44058 Binary files /dev/null and b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-videoinit.mp4 differ diff --git a/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-videoinit.mp4^headers^ b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-videoinit.mp4^headers^ new file mode 100644 index 0000000000..4030ea1d3d --- /dev/null +++ b/dom/media/mediasource/test/bipbop/bipbop_480_624kbps-videoinit.mp4^headers^ @@ -0,0 +1 @@ +Cache-Control: no-store diff --git a/dom/media/mediasource/test/mediasource.js b/dom/media/mediasource/test/mediasource.js index f65b6943f1..de3a48e7aa 100644 --- a/dom/media/mediasource/test/mediasource.js +++ b/dom/media/mediasource/test/mediasource.js @@ -54,8 +54,8 @@ function range(start, end) { function once(target, name, cb) { var p = new Promise(function(resolve, reject) { - target.addEventListener(name, function() { - target.removeEventListener(name, arguments.callee); + target.addEventListener(name, function onceEvent() { + target.removeEventListener(name, onceEvent); resolve(); }); }); @@ -115,4 +115,17 @@ SimpleTest.registerTimeoutFunction(function() { for (var a of document.getElementsByTagName("audio")) { a.mozDumpDebugInfo(); } -}); \ No newline at end of file +}); + +function waitUntilTime(target, targetTime) { + return new Promise(function(resolve, reject) { + target.addEventListener("waiting", function onwaiting() { + info("Got a waiting event at " + target.currentTime); + if (target.currentTime >= targetTime) { + ok(true, "Reached target time of: " + targetTime); + target.removeEventListener("waiting", onwaiting); + resolve(); + } + }); + }); +} diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index ba734fb560..e2515c7e73 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -1,5 +1,6 @@ [DEFAULT] -skip-if = e10s || buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined) +skip-if = buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined) +subsuite = media support-files = mediasource.js seek.webm seek.webm^headers^ @@ -19,89 +20,107 @@ support-files = bipbop/bipbop11.m4s bipbop/bipbop_audio11.m4s bipbop/bipbop_video11.m4s bipbop/bipbop12.m4s bipbop/bipbop_video12.m4s bipbop/bipbop13.m4s bipbop/bipbop_video13.m4s + bipbop/bipbopinit.mp4^headers^ bipbop/bipbop_audioinit.mp4^headers^ bipbop/bipbop_videoinit.mp4^headers^ + bipbop/bipbop1.m4s^headers^ bipbop/bipbop_audio1.m4s^headers^ bipbop/bipbop_video1.m4s^headers^ + bipbop/bipbop2.m4s^headers^ bipbop/bipbop_audio2.m4s^headers^ bipbop/bipbop_video2.m4s^headers^ + bipbop/bipbop3.m4s^headers^ bipbop/bipbop_audio3.m4s^headers^ bipbop/bipbop_video3.m4s^headers^ + bipbop/bipbop4.m4s^headers^ bipbop/bipbop_audio4.m4s^headers^ bipbop/bipbop_video4.m4s^headers^ + bipbop/bipbop5.m4s^headers^ bipbop/bipbop_audio5.m4s^headers^ bipbop/bipbop_video5.m4s^headers^ + bipbop/bipbop6.m4s^headers^ bipbop/bipbop_audio6.m4s^headers^ bipbop/bipbop_video6.m4s^headers^ + bipbop/bipbop7.m4s^headers^ bipbop/bipbop_audio7.m4s^headers^ bipbop/bipbop_video7.m4s^headers^ + bipbop/bipbop8.m4s^headers^ bipbop/bipbop_audio8.m4s^headers^ bipbop/bipbop_video8.m4s^headers^ + bipbop/bipbop9.m4s^headers^ bipbop/bipbop_audio9.m4s^headers^ bipbop/bipbop_video9.m4s^headers^ + bipbop/bipbop10.m4s^headers^ bipbop/bipbop_audio10.m4s^headers^ bipbop/bipbop_video10.m4s^headers^ + bipbop/bipbop11.m4s^headers^ bipbop/bipbop_audio11.m4s^headers^ bipbop/bipbop_video11.m4s^headers^ + bipbop/bipbop12.m4s^headers^ bipbop/bipbop_video12.m4s^headers^ + bipbop/bipbop13.m4s^headers^ bipbop/bipbop_video13.m4s^headers^ + aac20-48000-64000-init.mp4 aac20-48000-64000-init.mp4^headers^ + aac20-48000-64000-1.m4s aac20-48000-64000-1.m4s^headers^ + aac20-48000-64000-2.m4s aac20-48000-64000-2.m4s^headers^ + aac51-48000-128000-init.mp4 aac51-48000-128000-init.mp4^headers^ + aac51-48000-128000-1.m4s aac51-48000-128000-1.m4s^headers^ + aac51-48000-128000-2.m4s aac51-48000-128000-2.m4s^headers^ + bipbop/bipbop_480_624kbps-videoinit.mp4 bipbop/bipbop_480_624kbps-videoinit.mp4^headers^ + bipbop/bipbop_480_624kbps-video1.m4s bipbop/bipbop_480_624kbps-video1.m4s^headers^ + bipbop/bipbop_480_624kbps-video2.m4s bipbop/bipbop_480_624kbps-video2.m4s^headers^ +[test_AudioChange_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_BufferedSeek.html] -skip-if = true # bug 1182946 [test_BufferedSeek_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_BufferingWait.html] -skip-if = true # bug 1182946 +skip-if = toolkit == 'android' #timeout android bug 1199531 [test_BufferingWait_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_DrainOnMissingData_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_EndOfStream.html] skip-if = (true || toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187 and bug 1182946 [test_EndOfStream_mp4.html] -skip-if = (toolkit == 'android' || buildapp == 'mulet') || ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android' || buildapp == 'mulet')) # Not supported on xp and android 2.3 [test_DurationUpdated.html] -skip-if = true # bug 1182946 [test_DurationUpdated_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_FrameSelection.html] -skip-if = true # bug 1182946 +[test_FrameSelection_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_HaveMetadataUnbufferedSeek.html] -skip-if = true # bug 1182946 [test_HaveMetadataUnbufferedSeek_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_LoadedDataFired_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ -[test_LiveSeekable.html] +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_LoadedMetadataFired.html] -skip-if = true # bug 1182946 [test_LoadedMetadataFired_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_MediaSource.html] -skip-if = true # bug 1182946 [test_MediaSource_memory_reporting.html] [test_MediaSource_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_MediaSource_disabled.html] -[test_OnEvents.html] +[test_MultipleInitSegments.html] +[test_MultipleInitSegments_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_PlayEvents.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 +[test_ResumeAfterClearing_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SeekableAfterEndOfStream.html] -skip-if = true # bug 1182946 [test_SeekableAfterEndOfStream_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SeekableAfterEndOfStreamSplit.html] -skip-if = true # bug 1182946 [test_SeekableAfterEndOfStreamSplit_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SeekableBeforeEndOfStream.html] -skip-if = true # bug 1182946 [test_SeekableBeforeEndOfStream_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SeekableBeforeEndOfStreamSplit.html] -skip-if = true # bug 1182946 [test_SeekableBeforeEndOfStreamSplit_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SeekNoData_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SeekedEvent_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SeekTwice_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 +[test_Sequence_mp4.html] +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SetModeThrows.html] -skip-if = true # bug 1182946 [test_SplitAppendDelay.html] -skip-if = true # bug 1182946 [test_SplitAppendDelay_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_SplitAppend.html] -skip-if = true # bug 1182946 [test_SplitAppend_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_TimestampOffset_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_TruncatedDuration.html] -skip-if = true # bug 1182946 [test_TruncatedDuration_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_WaitingOnMissingData.html] skip-if = true # Disabled due to bug 1124493 and friends. WebM MSE is deprioritized. [test_WaitingOnMissingData_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 [test_WaitingToEndedTransition_mp4.html] -skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+ +skip-if = ((os == "win" && os_version == "5.1") || (toolkit == 'android')) # Not supported on xp and android 2.3 diff --git a/dom/media/mediasource/test/test_AudioChange_mp4.html b/dom/media/mediasource/test/test_AudioChange_mp4.html new file mode 100644 index 0000000000..95f48cadff --- /dev/null +++ b/dom/media/mediasource/test/test_AudioChange_mp4.html @@ -0,0 +1,72 @@ + + + + MSE: basic functionality + + + + + +
+
+
+ + diff --git a/dom/media/mediasource/test/test_FrameSelection_mp4.html b/dom/media/mediasource/test/test_FrameSelection_mp4.html new file mode 100644 index 0000000000..a5b8fa2903 --- /dev/null +++ b/dom/media/mediasource/test/test_FrameSelection_mp4.html @@ -0,0 +1,73 @@ + + + + MSE: Don't get stuck buffering for too long when we have frames to show + + + + + +

+
+ + diff --git a/dom/media/mediasource/test/test_MultipleInitSegments.html b/dom/media/mediasource/test/test_MultipleInitSegments.html new file mode 100644 index 0000000000..d97d72daee --- /dev/null +++ b/dom/media/mediasource/test/test_MultipleInitSegments.html @@ -0,0 +1,53 @@ + + + + + MSE: Append buffer with multiple init segments + + + + + +
+
+
+ + diff --git a/dom/media/mediasource/test/test_MultipleInitSegments_mp4.html b/dom/media/mediasource/test/test_MultipleInitSegments_mp4.html new file mode 100644 index 0000000000..8b1a8c7b77 --- /dev/null +++ b/dom/media/mediasource/test/test_MultipleInitSegments_mp4.html @@ -0,0 +1,52 @@ + + + + + MSE: Append buffer with multiple init segments + + + + + +
+
+
+ + diff --git a/dom/media/mediasource/test/test_ResumeAfterClearing_mp4.html b/dom/media/mediasource/test/test_ResumeAfterClearing_mp4.html new file mode 100644 index 0000000000..9b6622149c --- /dev/null +++ b/dom/media/mediasource/test/test_ResumeAfterClearing_mp4.html @@ -0,0 +1,55 @@ + + + + MSE: Don't get stuck buffering for too long when we have frames to show + + + + + +

+
+ + diff --git a/dom/media/mediasource/test/test_Sequence_mp4.html b/dom/media/mediasource/test/test_Sequence_mp4.html new file mode 100644 index 0000000000..f0542438d9 --- /dev/null +++ b/dom/media/mediasource/test/test_Sequence_mp4.html @@ -0,0 +1,39 @@ + + + + MSE: Don't get stuck buffering for too long when we have frames to show + + + + + +

+
+ + diff --git a/dom/media/omx/AudioOffloadPlayer.cpp b/dom/media/omx/AudioOffloadPlayer.cpp index 3bde13876a..05e5a45e67 100644 --- a/dom/media/omx/AudioOffloadPlayer.cpp +++ b/dom/media/omx/AudioOffloadPlayer.cpp @@ -346,7 +346,7 @@ status_t AudioOffloadPlayer::DoSeek() CHECK(mAudioSink.get()); AUDIO_OFFLOAD_LOG(LogLevel::Debug, - "DoSeek ( %lld )", mSeekTarget.GetTime().ToMicroseconds()); + ("DoSeek ( %lld )", mSeekTarget.GetTime().ToMicroseconds())); mReachedEOS = false; mPositionTimeMediaUs = -1; @@ -502,7 +502,6 @@ size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize) while (sizeRemaining > 0) { MediaSource::ReadOptions options; bool refreshSeekTime = false; - { android::Mutex::Autolock autoLock(mLock); @@ -519,7 +518,6 @@ size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize) } if (!mInputBuffer) { - status_t err; err = mSource->read(&mInputBuffer, &options); @@ -573,7 +571,7 @@ size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize) AUDIO_OFFLOAD_LOG(LogLevel::Debug, ("seek is updated during unlocking mLock")); } - if (refreshSeekTime) { + if (refreshSeekTime) { NotifyPositionChanged(); // need to adjust the mStartPosUs for offload decoding since parser diff --git a/dom/media/omx/MediaCodecProxy.cpp b/dom/media/omx/MediaCodecProxy.cpp index d94e97b1c5..16e3393583 100644 --- a/dom/media/omx/MediaCodecProxy.cpp +++ b/dom/media/omx/MediaCodecProxy.cpp @@ -90,10 +90,10 @@ MediaCodecProxy::MediaCodecProxy(sp aLooper, : mCodecLooper(aLooper) , mCodecMime(aMime) , mCodecEncoder(aEncoder) - , mMediaCodecLock("MediaCodecProxy::mMediaCodecLock") - , mPendingRequestMediaResource(false) + , mPromiseMonitor("MediaCodecProxy::mPromiseMonitor") { MOZ_ASSERT(mCodecLooper != nullptr, "ALooper should not be nullptr."); + mCodecPromise.SetMonitor(&mPromiseMonitor); } MediaCodecProxy::~MediaCodecProxy() @@ -134,6 +134,7 @@ MediaCodecProxy::AsyncAllocateVideoMediaCodec() mResourceClient->SetListener(this); mResourceClient->Acquire(); + mozilla::MonitorAutoLock lock(mPromiseMonitor); RefPtr p = mCodecPromise.Ensure(__func__); return p.forget(); } @@ -141,23 +142,16 @@ MediaCodecProxy::AsyncAllocateVideoMediaCodec() void MediaCodecProxy::ReleaseMediaCodec() { - mCodecPromise.RejectIfExists(true, __func__); - releaseCodec(); - - if (!mResourceClient) { - return; - } - - mozilla::MonitorAutoLock mon(mMediaCodecLock); - if (mPendingRequestMediaResource) { - mPendingRequestMediaResource = false; - mon.NotifyAll(); - } - + // At first, release mResourceClient's resource to prevent a conflict with + // mResourceClient's callback. if (mResourceClient) { mResourceClient->ReleaseResource(); mResourceClient = nullptr; } + + mozilla::MonitorAutoLock lock(mPromiseMonitor); + mCodecPromise.RejectIfExists(true, __func__); + releaseCodec(); } bool @@ -473,18 +467,12 @@ void MediaCodecProxy::ResourceReserved() { MCP_LOG("resourceReserved"); + mozilla::MonitorAutoLock lock(mPromiseMonitor); // Create MediaCodec if (!allocateCodec()) { - ReleaseMediaCodec(); mCodecPromise.RejectIfExists(true, __func__); return; } - - // Notify initialization waiting. - mozilla::MonitorAutoLock mon(mMediaCodecLock); - mPendingRequestMediaResource = false; - mon.NotifyAll(); - mCodecPromise.ResolveIfExists(true, __func__); } @@ -492,7 +480,7 @@ MediaCodecProxy::ResourceReserved() void MediaCodecProxy::ResourceReserveFailed() { - ReleaseMediaCodec(); + mozilla::MonitorAutoLock lock(mPromiseMonitor); mCodecPromise.RejectIfExists(true, __func__); } diff --git a/dom/media/omx/MediaCodecProxy.h b/dom/media/omx/MediaCodecProxy.h index 48af32920c..26435c55dd 100644 --- a/dom/media/omx/MediaCodecProxy.h +++ b/dom/media/omx/MediaCodecProxy.h @@ -163,6 +163,8 @@ private: bool mCodecEncoder; mozilla::MozPromiseHolder mCodecPromise; + // When mPromiseMonitor is held, mResourceClient's functions should not be called. + mozilla::Monitor mPromiseMonitor; // Media Resource Management RefPtr mResourceClient; @@ -174,9 +176,6 @@ private: //MediaCodec buffers to hold input/output data. Vector > mInputBuffers; Vector > mOutputBuffers; - - mozilla::Monitor mMediaCodecLock; - bool mPendingRequestMediaResource; }; } // namespace android diff --git a/dom/media/omx/MediaOmxCommonDecoder.cpp b/dom/media/omx/MediaOmxCommonDecoder.cpp index 771768921d..613fe02682 100644 --- a/dom/media/omx/MediaOmxCommonDecoder.cpp +++ b/dom/media/omx/MediaOmxCommonDecoder.cpp @@ -107,7 +107,6 @@ MediaOmxCommonDecoder::FirstFrameLoaded(nsAutoPtr aInfo, DisableStateMachineAudioOffloading(); return; } - PauseStateMachine(); if (mLogicallySeeking) { SeekTarget target = SeekTarget(mLogicalPosition, @@ -128,13 +127,7 @@ MediaOmxCommonDecoder::PauseStateMachine() MOZ_ASSERT(NS_IsMainThread()); DECODER_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__)); - if (mShuttingDown) { - return; - } - - if (!GetStateMachine()) { - return; - } + MOZ_ASSERT(GetStateMachine()); // enter dormant state GetStateMachine()->DispatchSetDormant(true); } @@ -232,7 +225,7 @@ MediaOmxCommonDecoder::ChangeState(PlayState aState) } void -MediaOmxCommonDecoder::CallSeek(SeekTarget aTarget) +MediaOmxCommonDecoder::CallSeek(const SeekTarget& aTarget) { if (!mAudioOffloadPlayer) { MediaDecoder::CallSeek(aTarget); diff --git a/dom/media/omx/MediaOmxCommonDecoder.h b/dom/media/omx/MediaOmxCommonDecoder.h index f784a5a82c..318480f639 100644 --- a/dom/media/omx/MediaOmxCommonDecoder.h +++ b/dom/media/omx/MediaOmxCommonDecoder.h @@ -26,7 +26,7 @@ public: void FirstFrameLoaded(nsAutoPtr aInfo, MediaDecoderEventVisibility aEventVisibility) override; void ChangeState(PlayState aState) override; - void CallSeek(SeekTarget aTarget) override; + void CallSeek(const SeekTarget& aTarget) override; void SetVolume(double aVolume) override; int64_t CurrentPosition() override; MediaDecoderOwner::NextFrameStatus NextFrameStatus() override; diff --git a/dom/media/omx/MediaOmxReader.cpp b/dom/media/omx/MediaOmxReader.cpp index 441e32e390..5da68bbdd1 100644 --- a/dom/media/omx/MediaOmxReader.cpp +++ b/dom/media/omx/MediaOmxReader.cpp @@ -266,8 +266,7 @@ void MediaOmxReader::HandleResourceAllocated() if (mLastParserDuration >= 0) { // Prefer the parser duration if we have it. - ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - mDecoder->SetMediaDuration(mLastParserDuration); + mInfo.mMetadataDuration = Some(TimeUnit::FromMicroseconds(mLastParserDuration)); } else { // MP3 parser failed to find a duration. // Set the total duration (the max of the audio and video track). @@ -381,9 +380,8 @@ bool MediaOmxReader::DecodeVideoFrame(bool &aKeyframeSkip, picture.height = (frame.Y.mHeight * mPicture.height) / mInitialFrame.height; } - MOZ_ASSERT(mStreamSource); // This is the approximate byte position in the stream. - int64_t pos = mStreamSource->Tell(); + int64_t pos = mStreamSource ? mStreamSource->Tell() : 0; RefPtr v; if (!frame.mGraphicBuffer) { @@ -496,9 +494,8 @@ bool MediaOmxReader::DecodeAudioData() MOZ_ASSERT(OnTaskQueue()); EnsureActive(); - MOZ_ASSERT(mStreamSource); // This is the approximate byte position in the stream. - int64_t pos = mStreamSource->Tell(); + int64_t pos = mStreamSource ? mStreamSource->Tell() : 0; // Read next frame MPAPI::AudioFrame source; @@ -542,7 +539,7 @@ MediaOmxReader::Seek(SeekTarget aTarget, int64_t aEndTime) // stream to the preceeding keyframe first, get the stream time, and then // seek the audio stream to match the video stream's time. Otherwise, the // audio and video streams won't be in sync after the seek. - mVideoSeekTimeUs = aTarget.mTime; + mVideoSeekTimeUs = aTarget.GetTime().ToMicroseconds(); RefPtr self = this; mSeekRequest.Begin(DecodeToFirstVideoData()->Then(OwnerThread(), __func__, [self] (MediaData* v) { @@ -555,7 +552,7 @@ MediaOmxReader::Seek(SeekTarget aTarget, int64_t aEndTime) self->mSeekPromise.Resolve(aTarget.GetTime(), __func__); })); } else { - mAudioSeekTimeUs = mVideoSeekTimeUs = GetTime().ToMicroseconds(); + mAudioSeekTimeUs = mVideoSeekTimeUs = aTarget.GetTime().ToMicroseconds(); mSeekPromise.Resolve(aTarget.GetTime(), __func__); } diff --git a/dom/media/omx/OMXCodecProxy.cpp b/dom/media/omx/OMXCodecProxy.cpp index 6fe150d46e..2fa410765a 100644 --- a/dom/media/omx/OMXCodecProxy.cpp +++ b/dom/media/omx/OMXCodecProxy.cpp @@ -64,6 +64,13 @@ OMXCodecProxy::OMXCodecProxy( OMXCodecProxy::~OMXCodecProxy() { + // At first, release mResourceClient's resource to prevent a conflict with + // mResourceClient's callback. + if (mResourceClient) { + mResourceClient->ReleaseResource(); + mResourceClient = nullptr; + } + mState = ResourceState::END; mCodecPromise.RejectIfExists(true, __func__); @@ -78,11 +85,6 @@ OMXCodecProxy::~OMXCodecProxy() // Complete all pending Binder ipc transactions IPCThreadState::self()->flushCommands(); - if (mResourceClient) { - mResourceClient->ReleaseResource(); - mResourceClient = nullptr; - } - mSource.clear(); free(mComponentName); mComponentName = nullptr; diff --git a/dom/media/omx/OMXCodecWrapper.cpp b/dom/media/omx/OMXCodecWrapper.cpp index 84c70f36a4..9d696d1b9d 100644 --- a/dom/media/omx/OMXCodecWrapper.cpp +++ b/dom/media/omx/OMXCodecWrapper.cpp @@ -17,7 +17,10 @@ #include "AudioChannelFormat.h" #include "GrallocImages.h" +#include "LayersLogging.h" +#include "libyuv.h" #include "mozilla/Monitor.h" +#include "mozilla/gfx/2D.h" #include "mozilla/layers/GrallocTextureClient.h" using namespace mozilla; @@ -407,6 +410,67 @@ ConvertGrallocImageToNV12(GrallocImage* aSource, uint8_t* aDestination) graphicBuffer->unlock(); } +static nsresult +ConvertSourceSurfaceToNV12(const RefPtr& aSurface, uint8_t* aDestination) +{ + uint32_t width = aSurface->GetSize().width; + uint32_t height = aSurface->GetSize().height; + + uint8_t* y = aDestination; + int yStride = width; + + uint8_t* uv = y + (yStride * height); + int uvStride = width / 2; + + SurfaceFormat format = aSurface->GetFormat(); + + if (!aSurface) { + CODEC_ERROR("Getting surface %s from image failed", Stringify(format).c_str()); + return NS_ERROR_FAILURE; + } + + RefPtr data = aSurface->GetDataSurface(); + if (!data) { + CODEC_ERROR("Getting data surface from %s image with %s (%s) surface failed", + Stringify(format).c_str(), Stringify(aSurface->GetType()).c_str(), + Stringify(aSurface->GetFormat()).c_str()); + return NS_ERROR_FAILURE; + } + + DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ); + if (!map.IsMapped()) { + CODEC_ERROR("Reading DataSourceSurface from %s image with %s (%s) surface failed", + Stringify(format).c_str(), Stringify(aSurface->GetType()).c_str(), + Stringify(aSurface->GetFormat()).c_str()); + return NS_ERROR_FAILURE; + } + + int rv; + switch (aSurface->GetFormat()) { + case SurfaceFormat::B8G8R8A8: + case SurfaceFormat::B8G8R8X8: + rv = libyuv::ARGBToNV12(static_cast(map.GetData()), + map.GetStride(), + y, yStride, + uv, uvStride, + width, height); + break; + default: + CODEC_ERROR("Unsupported SourceSurface format %s", + Stringify(aSurface->GetFormat()).c_str()); + NS_ASSERTION(false, "Unsupported SourceSurface format"); + return NS_ERROR_NOT_IMPLEMENTED; + } + + if (rv != 0) { + CODEC_ERROR("%s to I420 conversion failed", + Stringify(aSurface->GetFormat()).c_str()); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + nsresult OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight, int64_t aTimestamp, int aInputFlags, bool* aSendEOS) @@ -437,9 +501,10 @@ OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight, halFormat == GrallocImage::HAL_PIXEL_FORMAT_YCbCr_420_SP_VENUS, NS_ERROR_INVALID_ARG); } else { - // TODO: support RGB to YUV color conversion. - NS_ERROR("Unsupported input image type."); - return NS_ERROR_INVALID_ARG; + RefPtr surface = img->GetAsSourceSurface(); + NS_ENSURE_TRUE(surface->GetFormat() == SurfaceFormat::B8G8R8A8 || + surface->GetFormat() == SurfaceFormat::B8G8R8X8, + NS_ERROR_INVALID_ARG); } } @@ -477,6 +542,13 @@ OMXVideoEncoder::Encode(const Image* aImage, int aWidth, int aHeight, } else if (format == ImageFormat::PLANAR_YCBCR) { ConvertPlanarYCbCrToNV12(static_cast(img)->GetData(), dst); + } else { + RefPtr surface = img->GetAsSourceSurface(); + nsresult rv = ConvertSourceSurfaceToNV12(surface, dst); + + if (rv != NS_OK) { + return NS_ERROR_FAILURE; + } } } diff --git a/dom/media/omx/OMXCodecWrapper.h b/dom/media/omx/OMXCodecWrapper.h index 8d4002f7e7..b17f4ad5ee 100644 --- a/dom/media/omx/OMXCodecWrapper.h +++ b/dom/media/omx/OMXCodecWrapper.h @@ -283,7 +283,6 @@ private: */ class OMXVideoEncoder final : public OMXCodecWrapper { - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OMXVideoEncoder) public: // Types of output blob format. enum BlobFormat { diff --git a/dom/media/omx/OmxDecoder.cpp b/dom/media/omx/OmxDecoder.cpp index 2c6bd2d43e..fc2466c35e 100644 --- a/dom/media/omx/OmxDecoder.cpp +++ b/dom/media/omx/OmxDecoder.cpp @@ -19,6 +19,12 @@ #include #endif +#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 +#include +#else +#include +#endif + #include "mozilla/layers/GrallocTextureClient.h" #include "mozilla/layers/TextureClient.h" #include "mozilla/Preferences.h" @@ -230,11 +236,11 @@ OmxDecoder::AllocateMediaResources() #endif #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21 - mNativeWindowClient = new GonkNativeWindowClient(producer); + mNativeWindowClient = new Surface(producer); #elif defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17 - mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow->getBufferQueue()); + mNativeWindowClient = new Surface(mNativeWindow->getBufferQueue()); #else - mNativeWindowClient = new GonkNativeWindowClient(mNativeWindow); + mNativeWindowClient = new SurfaceTextureClient(mNativeWindow); #endif // Experience with OMX codecs is that only the HW decoders are diff --git a/dom/media/omx/OmxDecoder.h b/dom/media/omx/OmxDecoder.h index 66e4e9285b..da71b238cc 100644 --- a/dom/media/omx/OmxDecoder.h +++ b/dom/media/omx/OmxDecoder.h @@ -6,7 +6,6 @@ #include #include "GonkNativeWindow.h" -#include "GonkNativeWindowClient.h" #include "mozilla/layers/FenceUtils.h" #include "MP3FrameParser.h" #include "MPAPI.h" @@ -42,7 +41,8 @@ class OmxDecoder : public RefBase { AbstractMediaDecoder *mDecoder; sp mNativeWindow; - sp mNativeWindowClient; + sp mNativeWindowClient; + sp mVideoTrack; sp mVideoSource; sp mAudioOffloadTrack; diff --git a/dom/media/omx/RtspMediaCodecDecoder.cpp b/dom/media/omx/RtspMediaCodecDecoder.cpp deleted file mode 100644 index 51f1d2b626..0000000000 --- a/dom/media/omx/RtspMediaCodecDecoder.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* -*- 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 "RtspMediaCodecDecoder.h" - -#include "MediaDecoderStateMachine.h" -#include "RtspMediaResource.h" -#include "RtspMediaCodecReader.h" - -namespace mozilla { - -MediaDecoder* -RtspMediaCodecDecoder::Clone(MediaDecoderOwner* aOwner) -{ - return new RtspMediaCodecDecoder(aOwner); -} - -MediaOmxCommonReader* -RtspMediaCodecDecoder::CreateReader() -{ - return new RtspMediaCodecReader(this); -} - -MediaDecoderStateMachine* -RtspMediaCodecDecoder::CreateStateMachineFromReader(MediaOmxCommonReader* aReader) -{ - return new MediaDecoderStateMachine(this, aReader, - mResource->IsRealTime()); -} - -void -RtspMediaCodecDecoder::ChangeState(PlayState aState) -{ - MOZ_ASSERT(NS_IsMainThread()); - - MediaDecoder::ChangeState(aState); - - // Notify RTSP controller if the play state is ended. - // This is necessary for RTSP controller to transit its own state. - if (mPlayState == PLAY_STATE_ENDED) { - RefPtr resource = mResource->GetRtspPointer(); - if (resource) { - nsIStreamingProtocolController* controller = - resource->GetMediaStreamController(); - if (controller) { - controller->PlaybackEnded(); - } - } - } -} - -} // namespace mozilla - diff --git a/dom/media/omx/RtspMediaCodecDecoder.h b/dom/media/omx/RtspMediaCodecDecoder.h deleted file mode 100644 index e64f3f1b76..0000000000 --- a/dom/media/omx/RtspMediaCodecDecoder.h +++ /dev/null @@ -1,31 +0,0 @@ -/* -*- 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/. */ - -#if !defined(RtspMediaCodecDecoder_h_) -#define RtspMediaCodecDecoder_h_ - -#include "MediaOmxCommonDecoder.h" - -namespace mozilla { - -class RtspMediaCodecDecoder final : public MediaOmxCommonDecoder -{ -public: - explicit RtspMediaCodecDecoder(MediaDecoderOwner* aOwner) - : MediaOmxCommonDecoder(aOwner) {} - - MediaDecoder* Clone(MediaDecoderOwner* aOwner) override; - - MediaOmxCommonReader* CreateReader() override; - - MediaDecoderStateMachine* CreateStateMachineFromReader(MediaOmxCommonReader* aReader) override; - - void ChangeState(PlayState aState) override; -}; - -} // namespace mozilla - -#endif diff --git a/dom/media/omx/moz.build b/dom/media/omx/moz.build index 16d9ee85b9..7e7707faac 100644 --- a/dom/media/omx/moz.build +++ b/dom/media/omx/moz.build @@ -41,6 +41,7 @@ if CONFIG['MOZ_OMX_ENCODER']: 'OMXCodecDescriptorUtil.cpp', 'OMXCodecWrapper.cpp', ] + LOCAL_INCLUDES += ['/media/libyuv/include'] if 'rtsp' in CONFIG['NECKO_PROTOCOLS']: EXPORTS += [ @@ -70,10 +71,11 @@ include('/ipc/chromium/chromium-config.mozbuild') # - about attributes on forward declarations for types that are already # defined, which complains about an important MOZ_EXPORT for android::AString # - about multi-character constants which are used in codec-related code +# and are part of Android's libstagefright API style. if CONFIG['GNU_CC'] or CONFIG['CLANG_CL']: CXXFLAGS += [ '-Wno-error=attributes', - '-Wno-error=multichar' + '-Wno-multichar' ] FINAL_LIBRARY = 'xul' diff --git a/dom/media/platforms/agnostic/OpusDecoder.cpp b/dom/media/platforms/agnostic/OpusDecoder.cpp index 8835d5d842..e2cbba3139 100644 --- a/dom/media/platforms/agnostic/OpusDecoder.cpp +++ b/dom/media/platforms/agnostic/OpusDecoder.cpp @@ -103,10 +103,10 @@ OpusDataDecoder::DecodeHeader(const unsigned char* aData, size_t aLength) return NS_ERROR_FAILURE; } int channels = mOpusParser->mChannels; - // No channel mapping for more than 8 channels. - if (channels > 8) { - OPUS_DEBUG("No channel mapping for more than 8 channels. Source is %d channels", - channels); + + AudioConfig::ChannelLayout layout(channels); + if (!layout.IsValid()) { + OPUS_DEBUG("Invalid channel mapping. Source is %d channels", channels); return NS_ERROR_FAILURE; } diff --git a/dom/media/platforms/agnostic/VorbisDecoder.cpp b/dom/media/platforms/agnostic/VorbisDecoder.cpp index b8883d5614..259df44d78 100644 --- a/dom/media/platforms/agnostic/VorbisDecoder.cpp +++ b/dom/media/platforms/agnostic/VorbisDecoder.cpp @@ -105,6 +105,11 @@ VorbisDataDecoder::Init() ("Invalid Vorbis header: container and codec channels do not match!")); } + AudioConfig::ChannelLayout layout(mVorbisDsp.vi->channels); + if (!layout.IsValid()) { + return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__); + } + return InitPromise::CreateAndResolve(TrackInfo::kAudioTrack, __func__); } @@ -223,6 +228,9 @@ VorbisDataDecoder::DoDecode(MediaRawData* aSample) AudioConfig in(AudioConfig::ChannelLayout(channels, VorbisLayout(channels)), rate); AudioConfig out(channels, rate); + if (!in.IsValid() || !out.IsValid()) { + return -1; + } mAudioConverter = MakeUnique(in, out); } MOZ_ASSERT(mAudioConverter->CanWorkInPlace()); diff --git a/dom/media/platforms/android/AndroidDecoderModule.cpp b/dom/media/platforms/android/AndroidDecoderModule.cpp index 1d035749bb..cc09d2c228 100644 --- a/dom/media/platforms/android/AndroidDecoderModule.cpp +++ b/dom/media/platforms/android/AndroidDecoderModule.cpp @@ -4,12 +4,8 @@ #include "AndroidDecoderModule.h" #include "AndroidBridge.h" -#include "GLBlitHelper.h" -#include "GLContext.h" -#include "GLContextEGL.h" -#include "GLContextProvider.h" +#include "AndroidSurfaceTexture.h" #include "GLImages.h" -#include "GLLibraryEGL.h" #include "MediaData.h" #include "MediaInfo.h" @@ -121,7 +117,6 @@ public: void Cleanup() override { - mGLContext = nullptr; } nsresult Input(MediaRawData* aSample) override @@ -129,79 +124,13 @@ public: return MediaCodecDataDecoder::Input(aSample); } - bool WantCopy() const - { - // Allocating a texture is incredibly slow on PowerVR and may fail on - // emulators, see bug 1190379. - return mGLContext->Vendor() != GLVendor::Imagination && - mGLContext->Renderer() != GLRenderer::AndroidEmulator; - } - - EGLImage CopySurface(layers::Image* img) - { - mGLContext->MakeCurrent(); - - GLuint tex = CreateTextureForOffscreen(mGLContext, mGLContext->GetGLFormats(), - img->GetSize()); - - auto helper = mGLContext->BlitHelper(); - const gl::OriginPos destOrigin = gl::OriginPos::TopLeft; - if (!helper->BlitImageToTexture(img, img->GetSize(), tex, - LOCAL_GL_TEXTURE_2D, destOrigin)) - { - mGLContext->fDeleteTextures(1, &tex); - return nullptr; - } - - EGLint attribs[] = { - LOCAL_EGL_IMAGE_PRESERVED_KHR, LOCAL_EGL_TRUE, - LOCAL_EGL_NONE, LOCAL_EGL_NONE - }; - - EGLContext eglContext = static_cast(mGLContext.get())->GetEGLContext(); - EGLImage eglImage = sEGLLibrary.fCreateImage( - EGL_DISPLAY(), eglContext, LOCAL_EGL_GL_TEXTURE_2D_KHR, - reinterpret_cast(tex), attribs); - mGLContext->fDeleteTextures(1, &tex); - - return eglImage; - } - nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat, const TimeUnit& aDuration) override { - if (!EnsureGLContext()) { - return NS_ERROR_FAILURE; - } - RefPtr img = new SurfaceTextureImage(mSurfaceTexture.get(), mConfig.mDisplay, gl::OriginPos::BottomLeft); - if (WantCopy()) { - EGLImage eglImage = CopySurface(img); - if (!eglImage) { - return NS_ERROR_FAILURE; - } - - EGLSync eglSync = nullptr; - if (sEGLLibrary.IsExtensionSupported(GLLibraryEGL::KHR_fence_sync) && - mGLContext->IsExtensionSupported(GLContext::OES_EGL_sync)) - { - MOZ_ASSERT(mGLContext->IsCurrent()); - eglSync = sEGLLibrary.fCreateSync(EGL_DISPLAY(), - LOCAL_EGL_SYNC_FENCE, - nullptr); - MOZ_ASSERT(eglSync); - mGLContext->fFlush(); - } else { - NS_WARNING("No EGL fence support detected, rendering artifacts may occur!"); - } - - img = new layers::EGLImageImage(eglImage, eglSync, mConfig.mDisplay, - gl::OriginPos::TopLeft, true /* owns */); - } - nsresult rv; int32_t flags; NS_ENSURE_SUCCESS(rv = aInfo->Flags(&flags), rv); @@ -231,20 +160,9 @@ public: } protected: - bool EnsureGLContext() - { - if (mGLContext) { - return true; - } - - mGLContext = GLContextProvider::CreateHeadless(CreateContextFlags::NONE); - return mGLContext; - } - layers::ImageContainer* mImageContainer; const VideoInfo& mConfig; RefPtr mSurfaceTexture; - RefPtr mGLContext; }; class AudioDataDecoder : public MediaCodecDataDecoder @@ -283,6 +201,10 @@ public: int32_t numChannels; NS_ENSURE_SUCCESS(rv = aFormat->GetInteger(NS_LITERAL_STRING("channel-count"), &numChannels), rv); + AudioConfig::ChannelLayout layout(numChannels); + if (!layout.IsValid()) { + return NS_ERROR_FAILURE; + } int32_t sampleRate; NS_ENSURE_SUCCESS(rv = @@ -391,9 +313,12 @@ AndroidDecoderModule::CreateAudioDecoder( MediaFormat::LocalRef format; + LOG("CreateAudioFormat with mimeType=%s, mRate=%d, channels=%d", + aConfig.mMimeType.Data(), aConfig.mRate, aConfig.mChannels); + NS_ENSURE_SUCCESS(MediaFormat::CreateAudioFormat( aConfig.mMimeType, - aConfig.mBitDepth, + aConfig.mRate, aConfig.mChannels, &format), nullptr); @@ -464,10 +389,11 @@ MediaCodecDataDecoder::InitDecoder(Surface::Param aSurface) NS_ENSURE_SUCCESS(rv = ResetInputBuffers(), rv); NS_ENSURE_SUCCESS(rv = ResetOutputBuffers(), rv); - NS_NewNamedThread("MC Decoder", getter_AddRefs(mThread), - NS_NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop)); + rv = NS_NewNamedThread( + "MC Decoder", getter_AddRefs(mThread), + NS_NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop)); - return NS_OK; + return rv; } // This is in usec, so that's 10ms. @@ -522,7 +448,7 @@ MediaCodecDataDecoder::WaitForInput() } -MediaRawData* +already_AddRefed MediaCodecDataDecoder::PeekNextSample() { MonitorAutoLock lock(mMonitor); @@ -543,7 +469,7 @@ MediaCodecDataDecoder::PeekNextSample() } // We're not stopping or flushing, so try to get a sample. - return mQueue.front(); + return RefPtr(mQueue.front()).forget(); } nsresult @@ -584,7 +510,7 @@ MediaCodecDataDecoder::QueueSample(const MediaRawData* aSample) return res; } - mDurations.push(TimeUnit::FromMicroseconds(aSample->mDuration)); + mDurations.push_back(TimeUnit::FromMicroseconds(aSample->mDuration)); return NS_OK; } @@ -629,7 +555,7 @@ MediaCodecDataDecoder::GetOutputDuration() { MOZ_ASSERT(!mDurations.empty(), "Should have had a duration queued"); const TimeUnit duration = mDurations.front(); - mDurations.pop(); + mDurations.pop_front(); return duration; } @@ -661,12 +587,11 @@ MediaCodecDataDecoder::DecoderLoop() { bool isOutputDone = false; AutoLocalJNIFrame frame(jni::GetEnvForThread(), 1); - RefPtr sample; MediaFormat::LocalRef outputFormat(frame.GetEnv()); nsresult res = NS_OK; while (WaitForInput()) { - sample = PeekNextSample(); + RefPtr sample = PeekNextSample(); { MonitorAutoLock lock(mMonitor); @@ -682,7 +607,8 @@ MediaCodecDataDecoder::DecoderLoop() if (NS_SUCCEEDED(res)) { // We've fed this into the decoder, so remove it from the queue. MonitorAutoLock lock(mMonitor); - mQueue.pop(); + MOZ_RELEASE_ASSERT(mQueue.size(), "Queue may not be empty"); + mQueue.pop_front(); isOutputDone = false; } } @@ -757,18 +683,27 @@ MediaCodecDataDecoder::State() const return mState; } -void +bool MediaCodecDataDecoder::State(ModuleState aState) { - LOG("%s -> %s", ModuleStateStr(mState), ModuleStateStr(aState)); + bool ok = true; - if (aState == kDrainDecoder) { - MOZ_ASSERT(mState == kDrainQueue); + if (mState == kShutdown) { + ok = false; + } else if (mState == kStopping) { + ok = aState == kShutdown; + } else if (aState == kDrainDecoder) { + ok = mState == kDrainQueue; } else if (aState == kDrainWaitEOS) { - MOZ_ASSERT(mState == kDrainDecoder); + ok = mState == kDrainDecoder; } - mState = aState; + if (ok) { + LOG("%s -> %s", ModuleStateStr(mState), ModuleStateStr(aState)); + mState = aState; + } + + return ok; } void @@ -776,19 +711,15 @@ MediaCodecDataDecoder::ClearQueue() { mMonitor.AssertCurrentThreadOwns(); - while (!mQueue.empty()) { - mQueue.pop(); - } - while (!mDurations.empty()) { - mDurations.pop(); - } + mQueue.clear(); + mDurations.clear(); } nsresult MediaCodecDataDecoder::Input(MediaRawData* aSample) { MonitorAutoLock lock(mMonitor); - mQueue.push(aSample); + mQueue.push_back(aSample); lock.NotifyAll(); return NS_OK; @@ -810,7 +741,9 @@ nsresult MediaCodecDataDecoder::Flush() { MonitorAutoLock lock(mMonitor); - State(kFlushing); + if (!State(kFlushing)) { + return NS_OK; + } lock.Notify(); while (State() == kFlushing) { @@ -840,23 +773,23 @@ MediaCodecDataDecoder::Shutdown() { MonitorAutoLock lock(mMonitor); - if (!mThread || State() == kStopping) { - // Already shutdown or in the process of doing so - return NS_OK; - } - State(kStopping); lock.Notify(); - while (State() == kStopping) { + while (mThread && State() != kShutdown) { lock.Wait(); } - mThread->Shutdown(); - mThread = nullptr; + if (mThread) { + mThread->Shutdown(); + mThread = nullptr; + } - mDecoder->Stop(); - mDecoder->Release(); + if (mDecoder) { + mDecoder->Stop(); + mDecoder->Release(); + mDecoder = nullptr; + } return NS_OK; } diff --git a/dom/media/platforms/android/AndroidDecoderModule.h b/dom/media/platforms/android/AndroidDecoderModule.h index 5c86f20d21..f6bd0030e0 100644 --- a/dom/media/platforms/android/AndroidDecoderModule.h +++ b/dom/media/platforms/android/AndroidDecoderModule.h @@ -6,17 +6,17 @@ #define AndroidDecoderModule_h_ #include "PlatformDecoderModule.h" -#include "AndroidSurfaceTexture.h" #include "MediaCodec.h" +#include "SurfaceTexture.h" #include "TimeUnits.h" #include "mozilla/Monitor.h" -#include +#include namespace mozilla { -typedef std::queue> SampleQueue; +typedef std::deque> SampleQueue; class AndroidDecoderModule : public PlatformDecoderModule { public: @@ -101,7 +101,7 @@ protected: nsresult GetInputBuffer(JNIEnv* env, int index, jni::Object::LocalRef* buffer); bool WaitForInput(); - MediaRawData* PeekNextSample(); + already_AddRefed PeekNextSample(); nsresult QueueSample(const MediaRawData* aSample); nsresult QueueEOS(); void HandleEOS(int32_t aOutputStatus); @@ -110,7 +110,8 @@ protected: widget::sdk::MediaFormat::Param aFormat, int32_t aStatus); ModuleState State() const; - void State(ModuleState aState); + // Sets decoder state and returns whether the new state has become effective. + bool State(ModuleState aState); void DecoderLoop(); virtual void ClearQueue(); @@ -136,7 +137,7 @@ protected: SampleQueue mQueue; // Durations are stored in microseconds. - std::queue mDurations; + std::deque mDurations; }; } // namespace mozilla diff --git a/dom/media/platforms/apple/AppleATDecoder.cpp b/dom/media/platforms/apple/AppleATDecoder.cpp index 29612167ae..6d364c25d9 100644 --- a/dom/media/platforms/apple/AppleATDecoder.cpp +++ b/dom/media/platforms/apple/AppleATDecoder.cpp @@ -283,6 +283,9 @@ AppleATDecoder::DecodeSample(MediaRawData* aSample) if (mChannelLayout && !mAudioConverter) { AudioConfig in(*mChannelLayout.get(), rate); AudioConfig out(channels, rate); + if (!in.IsValid() || !out.IsValid()) { + return NS_ERROR_FAILURE; + } mAudioConverter = MakeUnique(in, out); } if (mAudioConverter) { diff --git a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp index fa0b6a90d1..802f5d9859 100644 --- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp +++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp @@ -185,6 +185,12 @@ FFmpegAudioDecoder::DecodePacket(MediaRawData* aSample) if (decoded) { uint32_t numChannels = mCodecContext->channels; + AudioConfig::ChannelLayout layout(numChannels); + if (!layout.IsValid()) { + mCallback->Error(); + return; + } + uint32_t samplingRate = mCodecContext->sample_rate; AlignedAudioBuffer audio = diff --git a/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp b/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp index 47227eb33e..9693a152c7 100644 --- a/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp +++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.cpp @@ -197,6 +197,10 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset, } // Update AudioInfo + AudioConfig::ChannelLayout layout(codec_channel_count); + if (!layout.IsValid()) { + return NS_ERROR_FAILURE; + } mAudioChannels = codec_channel_count; mAudioRate = codec_sample_rate; @@ -244,13 +248,22 @@ GonkAudioDecoderManager::Output(int64_t aStreamOffset, return NS_ERROR_NOT_AVAILABLE; } -nsresult -GonkAudioDecoderManager::Flush() +void +GonkAudioDecoderManager::ProcessFlush() { GADM_LOG("FLUSH<<<"); mAudioQueue.Reset(); GADM_LOG(">>>FLUSH"); - return GonkDecoderManager::Flush(); + GonkDecoderManager::ProcessFlush(); +} + +void +GonkAudioDecoderManager::ResetEOS() +{ + GADM_LOG("ResetEOS(<<<"); + mAudioQueue.Reset(); + GADM_LOG(">>>ResetEOS("); + GonkDecoderManager::ResetEOS(); } } // namespace mozilla diff --git a/dom/media/platforms/gonk/GonkAudioDecoderManager.h b/dom/media/platforms/gonk/GonkAudioDecoderManager.h index 67eb95e5f9..aa35d620ec 100644 --- a/dom/media/platforms/gonk/GonkAudioDecoderManager.h +++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.h @@ -31,7 +31,8 @@ public: nsresult Output(int64_t aStreamOffset, RefPtr& aOutput) override; - virtual nsresult Flush() override; + void ProcessFlush() override; + virtual void ResetEOS() override; const char* GetDescriptionName() const override { diff --git a/dom/media/platforms/gonk/GonkDecoderModule.cpp b/dom/media/platforms/gonk/GonkDecoderModule.cpp index 55f067f5d7..cf23104f38 100644 --- a/dom/media/platforms/gonk/GonkDecoderModule.cpp +++ b/dom/media/platforms/gonk/GonkDecoderModule.cpp @@ -56,7 +56,7 @@ PlatformDecoderModule::ConversionRequired GonkDecoderModule::DecoderNeedsConversion(const TrackInfo& aConfig) const { if (aConfig.IsVideo()) { - return kNeedAVCC; + return kNeedAnnexB; } else { return kNeedNone; } @@ -68,10 +68,12 @@ GonkDecoderModule::SupportsMimeType(const nsACString& aMimeType, { return aMimeType.EqualsLiteral("audio/mp4a-latm") || aMimeType.EqualsLiteral("audio/3gpp") || + aMimeType.EqualsLiteral("audio/amr-wb") || + aMimeType.EqualsLiteral("audio/mpeg") || aMimeType.EqualsLiteral("video/mp4") || aMimeType.EqualsLiteral("video/mp4v-es") || - aMimeType.EqualsLiteral("video/avc"); + aMimeType.EqualsLiteral("video/avc") || + aMimeType.EqualsLiteral("video/3gpp"); } } // namespace mozilla - diff --git a/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp b/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp index ec1eec4d57..6ed1739690 100644 --- a/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp +++ b/dom/media/platforms/gonk/GonkMediaDataDecoder.cpp @@ -27,12 +27,6 @@ using namespace android; namespace mozilla { -GonkDecoderManager::GonkDecoderManager(MediaTaskQueue* aTaskQueue) - : mMonitor("GonkDecoderManager") - , mTaskQueue(aTaskQueue) -{ -} - bool GonkDecoderManager::InitLoopers(MediaData::Type aType) { @@ -92,15 +86,15 @@ GonkDecoderManager::ProcessQueuedSamples() status_t rv; while (mQueuedSamples.Length()) { RefPtr data = mQueuedSamples.ElementAt(0); - { - rv = mDecoder->Input(reinterpret_cast(data->Data()), - data->Size(), - data->mTime, - 0, - INPUT_TIMEOUT_US); - } + rv = mDecoder->Input(reinterpret_cast(data->Data()), + data->Size(), + data->mTime, + 0, + INPUT_TIMEOUT_US); if (rv == OK) { mQueuedSamples.RemoveElementAt(0); + mWaitOutput.AppendElement(WaitOutputInfo(data->mOffset, data->mTime, + /* eos */ data->Data() == nullptr)); } else if (rv == -EAGAIN || rv == -ETIMEDOUT) { // In most cases, EAGAIN or ETIMEOUT are safe because OMX can't fill // buffer on time. @@ -119,13 +113,16 @@ GonkDecoderManager::Flush() GMDD_LOG("Decoder is not initialized"); return NS_ERROR_UNEXPECTED; } + + if (!mInitPromise.IsEmpty()) { + return NS_OK; + } + { MutexAutoLock lock(mMutex); mQueuedSamples.Clear(); } - mLastTime = 0; - MonitorAutoLock lock(mFlushMonitor); mIsFlushing = true; sp flush = new AMessage(kNotifyProcessFlush, id()); @@ -136,56 +133,6 @@ GonkDecoderManager::Flush() return NS_OK; } -nsresult -GonkDecoderManager::Input(MediaRawData* aSample) -{ - ReentrantMonitorAutoEnter mon(mMonitor); - nsRefPtr sample; - - if (!aSample) { - // It means EOS with empty sample. - sample = new MediaRawData(); - } else { - sample = aSample; - if (!PerformFormatSpecificProcess(sample)) { - return NS_ERROR_FAILURE; - } - } - - mQueueSample.AppendElement(sample); - - status_t rv; - while (mQueueSample.Length()) { - nsRefPtr data = mQueueSample.ElementAt(0); - { - ReentrantMonitorAutoExit mon_exit(mMonitor); - rv = SendSampleToOMX(data); - } - if (rv == OK) { - mQueueSample.RemoveElementAt(0); - } else if (rv == -EAGAIN || rv == -ETIMEDOUT) { - // In most cases, EAGAIN or ETIMEOUT are safe because OMX can't fill - // buffer on time. - return NS_OK; - } else { - return NS_ERROR_UNEXPECTED; - } - } - - return NS_OK; -} - -nsresult -GonkMediaDataDecoder::Init() -{ - sp decoder; - decoder = mManager->Init(mCallback); - mDecoder = decoder; - mDrainComplete = false; - - return NS_OK; -} - nsresult GonkDecoderManager::Shutdown() { @@ -200,8 +147,8 @@ GonkDecoderManager::Shutdown() return NS_OK; } -bool -GonkDecoderManager::HasQueuedSample() +size_t +GonkDecoderManager::NumQueuedSamples() { MutexAutoLock lock(mMutex); return mQueuedSamples.Length(); @@ -224,6 +171,8 @@ GonkDecoderManager::ProcessInput(bool aEndOfStream) mToDo->setInt32("input-eos", 1); } mDecoder->requestActivityNotification(mToDo); + } else if (aEndOfStream) { + mToDo->setInt32("input-eos", 1); } } else { GMDD_LOG("input processed: error#%d", rv); @@ -276,35 +225,30 @@ GonkDecoderManager::ProcessToDo(bool aEndOfStream) MOZ_ASSERT(mToDo.get() != nullptr); mToDo.clear(); - if (HasQueuedSample()) { - status_t pendingInput = ProcessQueuedSamples(); - if (pendingInput < 0) { - mDecodeCallback->Error(); - return; - } - if (!aEndOfStream && pendingInput <= MIN_QUEUED_SAMPLES) { - mDecodeCallback->InputExhausted(); - } + if (NumQueuedSamples() > 0 && ProcessQueuedSamples() < 0) { + mDecodeCallback->Error(); + return; } - nsresult rv = NS_OK; while (mWaitOutput.Length() > 0) { - nsRefPtr output; - int64_t offset = mWaitOutput.ElementAt(0); - rv = Output(offset, output); + RefPtr output; + WaitOutputInfo wait = mWaitOutput.ElementAt(0); + nsresult rv = Output(wait.mOffset, output); if (rv == NS_OK) { - mWaitOutput.RemoveElementAt(0); + MOZ_ASSERT(output); mDecodeCallback->Output(output); + UpdateWaitingList(output->mTime); } else if (rv == NS_ERROR_ABORT) { - GMDD_LOG("eos output"); - mWaitOutput.RemoveElementAt(0); - MOZ_ASSERT(mQueuedSamples.IsEmpty()); - MOZ_ASSERT(mWaitOutput.IsEmpty()); // EOS + MOZ_ASSERT(mQueuedSamples.IsEmpty()); if (output) { mDecodeCallback->Output(output); + UpdateWaitingList(output->mTime); } + MOZ_ASSERT(mWaitOutput.Length() == 1); + mWaitOutput.RemoveElementAt(0); mDecodeCallback->DrainComplete(); + ResetEOS(); return; } else if (rv == NS_ERROR_NOT_AVAILABLE) { break; @@ -314,12 +258,33 @@ GonkDecoderManager::ProcessToDo(bool aEndOfStream) } } - if (HasQueuedSample() || mWaitOutput.Length() > 0) { + if (!aEndOfStream && NumQueuedSamples() <= MIN_QUEUED_SAMPLES) { + mDecodeCallback->InputExhausted(); + // No need to shedule todo task this time because InputExhausted() will + // cause Input() to be invoked and do it for us. + return; + } + + if (NumQueuedSamples() || mWaitOutput.Length() > 0) { mToDo = new AMessage(kNotifyDecoderActivity, id()); + if (aEndOfStream) { + mToDo->setInt32("input-eos", 1); + } mDecoder->requestActivityNotification(mToDo); } } +void +GonkDecoderManager::ResetEOS() +{ + // After eos, android::MediaCodec needs to be flushed to receive next input + mWaitOutput.Clear(); + if (mDecoder->flush() != OK) { + GMDD_LOG("flush error"); + mDecodeCallback->Error(); + } +} + void GonkDecoderManager::onMessageReceived(const sp &aMessage) { @@ -368,8 +333,7 @@ GonkDecoderManager::OnTaskLooper() GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager, FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback) - : mTaskQueue(aTaskQueue) - , mManager(aManager) + : mManager(aManager) { MOZ_COUNT_CTOR(GonkMediaDataDecoder); mManager->SetDecodeCallback(aCallback); @@ -408,11 +372,6 @@ GonkMediaDataDecoder::Input(MediaRawData* aSample) nsresult GonkMediaDataDecoder::Flush() { - // Flush the input task queue. This cancels all pending Decode() calls. - // Note this blocks until the task queue finishes its current job, if - // it's executing at all. Note the MP4Reader ignores all output while - // flushing. - mTaskQueue->Flush(); return mManager->Flush(); } diff --git a/dom/media/platforms/gonk/GonkMediaDataDecoder.h b/dom/media/platforms/gonk/GonkMediaDataDecoder.h index 40fded26d1..2f440526d9 100644 --- a/dom/media/platforms/gonk/GonkMediaDataDecoder.h +++ b/dom/media/platforms/gonk/GonkMediaDataDecoder.h @@ -40,8 +40,8 @@ public: // Shutdown decoder and rejects the init promise. virtual nsresult Shutdown(); - // True if sample is queued. - bool HasQueuedSample(); + // How many samples are waiting for processing. + size_t NumQueuedSamples(); // Set callback for decoder events, such as requesting more input, // returning output, or reporting error. @@ -53,7 +53,7 @@ public: protected: GonkDecoderManager() : mMutex("GonkDecoderManager") - , mLastTime(0) + , mLastTime(INT64_MIN) , mFlushMonitor("GonkDecoderManager::Flush") , mIsFlushing(false) , mDecodeCallback(nullptr) @@ -75,8 +75,9 @@ protected: int32_t ProcessQueuedSamples(); void ProcessInput(bool aEndOfStream); - void ProcessFlush(); + virtual void ProcessFlush(); void ProcessToDo(bool aEndOfStream); + virtual void ResetEOS(); RefPtr mCodecSpecificData; @@ -122,8 +123,19 @@ protected: // forbidden by mDecoder. android::sp mToDo; - // Stores the offset of every output that needs to be read from mDecoder. - nsTArray mWaitOutput; + // Stores sample info for output buffer processing later. + struct WaitOutputInfo { + WaitOutputInfo(int64_t aOffset, int64_t aTimestamp, bool aEOS) + : mOffset(aOffset) + , mTimestamp(aTimestamp) + , mEOS(aEOS) + {} + const int64_t mOffset; + const int64_t mTimestamp; + const bool mEOS; + }; + + nsTArray mWaitOutput; MediaDataDecoderCallback* mDecodeCallback; // Reports decoder output or error. @@ -195,7 +207,6 @@ public: } private: - RefPtr mTaskQueue; android::sp mManager; }; diff --git a/dom/media/platforms/gonk/GonkVideoDecoderManager.h b/dom/media/platforms/gonk/GonkVideoDecoderManager.h index 8537d96397..343bb2a5ce 100644 --- a/dom/media/platforms/gonk/GonkVideoDecoderManager.h +++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.h @@ -49,16 +49,6 @@ public: nsresult Shutdown() override; - // Bug 1199809: workaround to avoid sending the graphic buffer by making a - // copy of output buffer after calling flush(). Bug 1203859 was created to - // reimplementing Gonk PDM on top of OpenMax IL directly. Its buffer - // management will work better with Gecko and solve problems like this. - nsresult Flush() override - { - mNeedsCopyBuffer = true; - return GonkDecoderManager::Flush(); - } - const char* GetDescriptionName() const override { return "gonk video decoder"; @@ -66,6 +56,17 @@ public: static void RecycleCallback(TextureClient* aClient, void* aClosure); +protected: + // Bug 1199809: workaround to avoid sending the graphic buffer by making a + // copy of output buffer after calling flush(). Bug 1203859 was created to + // reimplementing Gonk PDM on top of OpenMax IL directly. Its buffer + // management will work better with Gecko and solve problems like this. + void ProcessFlush() override + { + mNeedsCopyBuffer = true; + GonkDecoderManager::ProcessFlush(); + } + private: struct FrameInfo { diff --git a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp index 6e1ba8544a..779c71ca19 100644 --- a/dom/media/platforms/wmf/WMFAudioMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFAudioMFTManager.cpp @@ -195,6 +195,11 @@ WMFAudioMFTManager::UpdateOutputType() hr = type->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &mAudioChannels); NS_ENSURE_TRUE(SUCCEEDED(hr), hr); + AudioConfig::ChannelLayout layout(mAudioChannels); + if (!layout.IsValid()) { + return E_FAIL; + } + return S_OK; } diff --git a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp index 7c49791068..db263df98c 100644 --- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp @@ -186,7 +186,7 @@ WMFMediaDataDecoder::Drain() MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown); nsCOMPtr runnable = - NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain); + NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain); mTaskQueue->Dispatch(runnable.forget()); return NS_OK; } diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index 86b456355e..543799dcf9 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -235,7 +235,8 @@ bool WMFVideoMFTManager::InitInternal(bool aForceD3D9) { mUseHwAccel = false; // default value; changed if D3D setup succeeds. - bool useDxva = InitializeDXVA(aForceD3D9); + bool useDxva = InitializeDXVA(aForceD3D9 || + mStreamType == VP8 || mStreamType == VP9); RefPtr decoder(new MFTDecoder()); diff --git a/dom/media/platforms/wrappers/FuzzingWrapper.cpp b/dom/media/platforms/wrappers/FuzzingWrapper.cpp index 6b87a8fb5a..0cb776c582 100644 --- a/dom/media/platforms/wrappers/FuzzingWrapper.cpp +++ b/dom/media/platforms/wrappers/FuzzingWrapper.cpp @@ -50,11 +50,12 @@ DecoderFuzzingWrapper::Input(MediaRawData* aData) nsresult DecoderFuzzingWrapper::Flush() { - DFW_LOGV(""); + DFW_LOGV("Calling mDecoder[%p]->Flush()", mDecoder.get()); MOZ_ASSERT(mDecoder); // Flush may output some frames (though unlikely). // Flush may block a bit, it's ok if we output some frames in the meantime. nsresult result = mDecoder->Flush(); + DFW_LOGV("mDecoder[%p]->Flush() -> result=%u", mDecoder.get(), uint32_t(result)); // Clear any delayed output we may have. mCallbackWrapper->ClearDelayedOutput(); return result; @@ -254,13 +255,28 @@ void DecoderCallbackFuzzingWrapper::ScheduleOutputDelayedFrame() { MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + if (mDelayedOutputRequest.Exists()) { + // A delayed output is already scheduled, no need for more than one timer. + return; + } RefPtr self = this; - mDelayedOutputTimer->WaitUntil( - mPreviousOutput + mFrameOutputMinimumInterval, - __func__) - ->Then(mTaskQueue, __func__, - [self] () -> void { self->OutputDelayedFrame(); }, - [self] () -> void { self->OutputDelayedFrame(); }); + mDelayedOutputRequest.Begin( + mDelayedOutputTimer->WaitUntil( + mPreviousOutput + mFrameOutputMinimumInterval, + __func__) + ->Then(mTaskQueue, __func__, + [self] () -> void { + if (self->mDelayedOutputRequest.Exists()) { + self->mDelayedOutputRequest.Complete(); + self->OutputDelayedFrame(); + } + }, + [self] () -> void { + if (self->mDelayedOutputRequest.Exists()) { + self->mDelayedOutputRequest.Complete(); + self->ClearDelayedOutput(); + } + })); } void @@ -300,11 +316,16 @@ void DecoderCallbackFuzzingWrapper::ClearDelayedOutput() { if (!mTaskQueue->IsCurrentThreadIn()) { + DFW_LOGV("(dispatching self)"); nsCOMPtr task = NS_NewRunnableMethod(this, &DecoderCallbackFuzzingWrapper::ClearDelayedOutput); mTaskQueue->Dispatch(task.forget()); return; } + DFW_LOGV(""); + // In case a timer hasn't lapsed yet, before destroying the timer and its + // attached waitUntil() promise, the 'Then' request must be disconnected. + mDelayedOutputRequest.DisconnectIfExists(); mDelayedOutputTimer = nullptr; mDelayedOutput.clear(); } @@ -312,10 +333,16 @@ DecoderCallbackFuzzingWrapper::ClearDelayedOutput() void DecoderCallbackFuzzingWrapper::Shutdown() { - DFW_LOGV("Shutting down mTaskQueue"); + CFW_LOGV("Clear delayed output (if any) before shutting down mTaskQueue"); + ClearDelayedOutput(); + // Await idle here, so that 'ClearDelayedOutput' runs to completion before + // the task queue is shutdown (and tasks can't be queued anymore). + mTaskQueue->AwaitIdle(); + + CFW_LOGV("Shutting down mTaskQueue"); mTaskQueue->BeginShutdown(); mTaskQueue->AwaitIdle(); - DFW_LOGV("mTaskQueue shut down"); + CFW_LOGV("mTaskQueue shut down"); } } // namespace mozilla diff --git a/dom/media/platforms/wrappers/FuzzingWrapper.h b/dom/media/platforms/wrappers/FuzzingWrapper.h index 4806f21fde..f6b34624bf 100644 --- a/dom/media/platforms/wrappers/FuzzingWrapper.h +++ b/dom/media/platforms/wrappers/FuzzingWrapper.h @@ -81,6 +81,7 @@ private: typedef Pair, bool> MediaDataAndInputExhausted; std::deque mDelayedOutput; RefPtr mDelayedOutputTimer; + MozPromiseRequestHolder mDelayedOutputRequest; // If draining, a 'DrainComplete' will be sent after all delayed frames have // been output. bool mDraining; diff --git a/dom/media/systemservices/CamerasChild.h b/dom/media/systemservices/CamerasChild.h index 02b59b473d..ca51a60147 100644 --- a/dom/media/systemservices/CamerasChild.h +++ b/dom/media/systemservices/CamerasChild.h @@ -13,6 +13,7 @@ #include "mozilla/camera/PCamerasChild.h" #include "mozilla/camera/PCamerasParent.h" #include "mozilla/Mutex.h" +#include "base/singleton.h" #include "nsCOMPtr.h" // conflicts with #include of scoped_ptr.h @@ -79,24 +80,21 @@ public: ~CamerasSingleton(); static OffTheBooksMutex& Mutex() { - return GetInstance().mCamerasMutex; + return gTheInstance.get()->mCamerasMutex; } static CamerasChild*& Child() { Mutex().AssertCurrentThreadOwns(); - return GetInstance().mCameras; + return gTheInstance.get()->mCameras; } static nsCOMPtr& Thread() { Mutex().AssertCurrentThreadOwns(); - return GetInstance().mCamerasChildThread; + return gTheInstance.get()->mCamerasChildThread; } private: - static CamerasSingleton& GetInstance() { - static CamerasSingleton instance; - return instance; - } + static Singleton gTheInstance; // Reinitializing CamerasChild will change the pointers below. // We don't want this to happen in the middle of preparing IPC. diff --git a/dom/media/systemservices/CamerasParent.cpp b/dom/media/systemservices/CamerasParent.cpp index fddf0e2094..47330ddc54 100644 --- a/dom/media/systemservices/CamerasParent.cpp +++ b/dom/media/systemservices/CamerasParent.cpp @@ -253,7 +253,7 @@ CamerasParent::DeliverFrameOverIPC(CaptureEngine cap_engine, ShmemBuffer shMemBuff = mShmemPool.Get(this, size); if (!shMemBuff.Valid()) { - LOG(("Video shmem is not writeable in DeliverFrame")); + LOG(("No usable Video shmem in DeliverFrame (out of buffers?)")); // We can skip this frame if we run out of buffers, it's not a real error. return 0; } @@ -267,6 +267,7 @@ CamerasParent::DeliverFrameOverIPC(CaptureEngine cap_engine, return -1; } } else { + MOZ_ASSERT(buffer.Valid()); // ShmemBuffer was available, we're all good. A single copy happened // in the original webrtc callback. if (!SendDeliverFrame(cap_engine, cap_id, @@ -297,7 +298,7 @@ CallbackHelper::DeliverFrame(unsigned char* buffer, ShmemBuffer shMemBuffer = mParent->GetBuffer(size); if (!shMemBuffer.Valid()) { // Either we ran out of buffers or they're not the right size yet - LOG(("Video shmem is not available in DeliverFrame")); + LOG(("Correctly sized Video shmem not available in DeliverFrame")); // We will do the copy into a(n extra) temporary buffer inside // the DeliverFrameRunnable constructor. } else { diff --git a/dom/media/systemservices/MediaChild.cpp b/dom/media/systemservices/MediaChild.cpp index 47a24bb39f..5d04f5f823 100644 --- a/dom/media/systemservices/MediaChild.cpp +++ b/dom/media/systemservices/MediaChild.cpp @@ -111,5 +111,5 @@ DeallocPMediaChild(media::PMediaChild *aActor) return true; } -} -} +} // namespace media +} // namespace mozilla diff --git a/dom/media/systemservices/MediaSystemResourceManager.cpp b/dom/media/systemservices/MediaSystemResourceManager.cpp index 75fd5f54a8..4d833dd239 100644 --- a/dom/media/systemservices/MediaSystemResourceManager.cpp +++ b/dom/media/systemservices/MediaSystemResourceManager.cpp @@ -226,9 +226,7 @@ MediaSystemResourceManager::AcquireSyncNoWait(MediaSystemResourceClient* aClient HandleAcquireResult(aClient->mId, false); return false; } - if (!aClient || - !client || - client != aClient) { + if (!client || client != aClient) { HandleAcquireResult(aClient->mId, false); return false; } diff --git a/dom/media/systemservices/MediaSystemResourceManager.h b/dom/media/systemservices/MediaSystemResourceManager.h index fa9f32147a..1160aa2fd1 100644 --- a/dom/media/systemservices/MediaSystemResourceManager.h +++ b/dom/media/systemservices/MediaSystemResourceManager.h @@ -20,7 +20,7 @@ namespace mozilla { namespace media { class MediaSystemResourceManagerChild; -} +} // namespace media class MediaSystemResourceClient; class MediaSystemResourceReservationListener; diff --git a/dom/media/systemservices/MediaSystemResourceManagerChild.h b/dom/media/systemservices/MediaSystemResourceManagerChild.h index 57c451b936..51d4de8d13 100644 --- a/dom/media/systemservices/MediaSystemResourceManagerChild.h +++ b/dom/media/systemservices/MediaSystemResourceManagerChild.h @@ -15,7 +15,7 @@ class MediaSystemResourceManager; namespace ipc { class BackgroundChildImpl; -} +} // namespace ipc namespace media { @@ -60,7 +60,7 @@ private: friend class mozilla::ipc::BackgroundChildImpl; }; -} // namespatce media -} // namespatce mozilla +} // namespace media +} // namespace mozilla #endif diff --git a/dom/media/systemservices/MediaSystemResourceManagerParent.h b/dom/media/systemservices/MediaSystemResourceManagerParent.h index 693bcd973b..9080cb3855 100644 --- a/dom/media/systemservices/MediaSystemResourceManagerParent.h +++ b/dom/media/systemservices/MediaSystemResourceManagerParent.h @@ -51,7 +51,7 @@ private: nsClassHashtable mResourceRequests; }; -} // namespatce media -} // namespatce mozilla +} // namespace media +} // namespace mozilla #endif diff --git a/dom/media/systemservices/MediaSystemResourceService.cpp b/dom/media/systemservices/MediaSystemResourceService.cpp index c7f01fb375..d7c1569bcf 100644 --- a/dom/media/systemservices/MediaSystemResourceService.cpp +++ b/dom/media/systemservices/MediaSystemResourceService.cpp @@ -219,7 +219,6 @@ MediaSystemResourceService::RemoveRequests(media::MediaSystemResourceManagerPare for (it = waitingRequests.begin(); it != waitingRequests.end();) { if ((*it).mParent == aParent) { it = waitingRequests.erase(it); - return; } else { it++; } diff --git a/dom/media/systemservices/MediaSystemResourceService.h b/dom/media/systemservices/MediaSystemResourceService.h index 9329cd9304..06e1b537cc 100644 --- a/dom/media/systemservices/MediaSystemResourceService.h +++ b/dom/media/systemservices/MediaSystemResourceService.h @@ -17,7 +17,7 @@ namespace mozilla { namespace media { class MediaSystemResourceManagerParent; -} +} // namespace media /** * Manage media system resource allocation requests within system. diff --git a/dom/media/systemservices/ShmemPool.cpp b/dom/media/systemservices/ShmemPool.cpp index c83c41ebf0..e4d3a054ff 100644 --- a/dom/media/systemservices/ShmemPool.cpp +++ b/dom/media/systemservices/ShmemPool.cpp @@ -40,12 +40,14 @@ mozilla::ShmemBuffer ShmemPool::GetIfAvailable(size_t aSize) ShmemBuffer& res = mShmemPool[mPoolFree - 1]; if (!res.mInitialized) { + LOG(("No free preallocated Shmem")); return ShmemBuffer(); } MOZ_ASSERT(res.mShmem.IsWritable(), "Pool in Shmem is not writable?"); if (res.mShmem.Size() < aSize) { + LOG(("Free Shmem but not of the right size")); return ShmemBuffer(); } @@ -71,30 +73,45 @@ mozilla::ShmemBuffer ShmemPool::Get(T* aInstance, size_t aSize) return ShmemBuffer(); } - ShmemBuffer res = Move(mShmemPool[mPoolFree - 1]); + ShmemBuffer& res = mShmemPool[mPoolFree - 1]; if (!res.mInitialized) { - LOG(("Initiaizing new Shmem in pool")); - aInstance->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &res.mShmem); + LOG(("Initializing new Shmem in pool")); + if (!aInstance->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &res.mShmem)) { + LOG(("Failure allocating new Shmem buffer")); + return ShmemBuffer(); + } res.mInitialized = true; } - MOZ_ASSERT(res.mShmem.IsWritable(), "Pool in Shmem is not writable?"); + MOZ_ASSERT(res.mShmem.IsWritable(), "Shmem in Pool is not writable?"); // Prepare buffer, increase size if needed (we never shrink as we don't // maintain seperate sized pools and we don't want to keep reallocating) if (res.mShmem.Size() < aSize) { LOG(("Size change/increase in Shmem Pool")); aInstance->DeallocShmem(res.mShmem); + res.mInitialized = false; // this may fail; always check return value if (!aInstance->AllocShmem(aSize, SharedMemory::TYPE_BASIC, &res.mShmem)) { - LOG(("Failure allocating new size Shmem buffer")); + LOG(("Failure allocating resized Shmem buffer")); return ShmemBuffer(); + } else { + res.mInitialized = true; } } + MOZ_ASSERT(res.mShmem.IsWritable(), "Shmem in Pool is not writable post resize?"); + mPoolFree--; - return res; +#ifdef DEBUG + size_t poolUse = mShmemPool.Length() - mPoolFree; + if (poolUse > mMaxPoolUse) { + mMaxPoolUse = poolUse; + LOG(("Maximum ShmemPool use increased: %d buffers", mMaxPoolUse)); + } +#endif + return Move(res); } void ShmemPool::Put(ShmemBuffer&& aShmem) @@ -103,6 +120,12 @@ void ShmemPool::Put(ShmemBuffer&& aShmem) MOZ_ASSERT(mPoolFree < mShmemPool.Length()); mShmemPool[mPoolFree] = Move(aShmem); mPoolFree++; +#ifdef DEBUG + size_t poolUse = mShmemPool.Length() - mPoolFree; + if (poolUse > 0) { + LOG(("ShmemPool usage reduced to %d buffers", poolUse)); + } +#endif } template diff --git a/dom/media/tests/crashtests/crashtests.list b/dom/media/tests/crashtests/crashtests.list index e967583d3c..2acec66506 100644 --- a/dom/media/tests/crashtests/crashtests.list +++ b/dom/media/tests/crashtests/crashtests.list @@ -3,14 +3,14 @@ default-preferences pref(media.peerconnection.enabled,true) pref(media.navigato load 780790.html load 791270.html load 791278.html -skip-if(Android||B2G) load 791330.html # bug 909925 +load 791330.html load 799419.html load 802982.html load 812785.html load 834100.html load 836349.html load 837324.html -skip-if(Android||B2G) load 855796.html # bug 909925 +load 855796.html load 860143.html load 861958.html -skip-if(Android||B2G) load 863929.html # bug 909925 +load 863929.html diff --git a/dom/media/tests/mochitest/identity/mochitest.ini b/dom/media/tests/mochitest/identity/mochitest.ini index af4de443ce..014f873124 100644 --- a/dom/media/tests/mochitest/identity/mochitest.ini +++ b/dom/media/tests/mochitest/identity/mochitest.ini @@ -1,8 +1,10 @@ [DEFAULT] -# strictContentSandbox - bug 1042735, Android 2.3 - bug 981881 # won't run on b2g desktop tests - bug 1119993 # broken HTTPS on b2g emulator - bug 1135339 -skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' || android_version == '18' || (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'b2g' && toolkit == 'gonk') +# Android 4.3 - bug 981881 +# Exit code -11 for linux/opt/non-e10s - bug 1256284 +subsuite = media +skip-if = android_version == '18' || (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'b2g' && toolkit == 'gonk') || buildapp == 'mulet' || (os == 'linux' && !debug && !e10s) support-files = /.well-known/idp-proxy/idp.js identityPcTest.js diff --git a/dom/media/wave/WaveDemuxer.cpp b/dom/media/wave/WaveDemuxer.cpp index fb7d830e1f..ab1c9eb637 100644 --- a/dom/media/wave/WaveDemuxer.cpp +++ b/dom/media/wave/WaveDemuxer.cpp @@ -120,7 +120,10 @@ WAVTrackDemuxer::Init() } } else if (aChunkName == LIST_CODE) { mHeaderParser.Reset(); - uint32_t endOfListChunk = mOffset + aChunkSize; + uint64_t endOfListChunk = static_cast(mOffset) + aChunkSize; + if (endOfListChunk > UINT32_MAX) { + return false; + } if (!ListChunkParserInit(aChunkSize)) { mOffset = endOfListChunk; } @@ -234,9 +237,9 @@ WAVTrackDemuxer::ListChunkParserInit(uint32_t aChunkSize) return false; } - const char* mRawData = reinterpret_cast(mChunkData->Data()); + const char* rawData = reinterpret_cast(mChunkData->Data()); - nsCString val(mRawData, length); + nsCString val(rawData, length); if (length > 0 && val[length - 1] == '\0') { val.SetLength(length - 1); } @@ -406,11 +409,12 @@ WAVTrackDemuxer::StreamLength() const TimeUnit WAVTrackDemuxer::Duration() const { - if (!mDataLength) { + if (!mDataLength ||!mChannels || !mSampleFormat) { return TimeUnit(); } - int64_t numSamples = mDataLength * 8 / mChannels / mSampleFormat; + int64_t numSamples = + static_cast(mDataLength) * 8 / mChannels / mSampleFormat; int64_t numUSeconds = USECS_PER_S * numSamples / mSamplesPerSecond; @@ -424,10 +428,11 @@ WAVTrackDemuxer::Duration() const TimeUnit WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const { - if (!mSamplesPerSecond) { + if (!mSamplesPerSecond || !mSamplesPerChunk) { return TimeUnit(); } - const double usPerDataChunk = USECS_PER_S * mSamplesPerChunk / + const double usPerDataChunk = USECS_PER_S * + static_cast(mSamplesPerChunk) / mSamplesPerSecond; return TimeUnit::FromMicroseconds(aNumDataChunks * usPerDataChunk); } @@ -435,7 +440,7 @@ WAVTrackDemuxer::Duration(int64_t aNumDataChunks) const TimeUnit WAVTrackDemuxer::DurationFromBytes(uint32_t aNumBytes) const { - if (!mSamplesPerSecond) { + if (!mSamplesPerSecond || !mChannels || !mSampleFormat) { return TimeUnit(); } @@ -586,6 +591,9 @@ WAVTrackDemuxer::ChunkIndexFromOffset(int64_t aOffset) const int64_t WAVTrackDemuxer::ChunkIndexFromTime(const media::TimeUnit& aTime) const { + if (!mSamplesPerChunk || !mSamplesPerSecond) { + return 0; + } int64_t chunkIndex = (aTime.ToSeconds() * mSamplesPerSecond / mSamplesPerChunk) - 1; return chunkIndex; @@ -651,6 +659,7 @@ RIFFParser::RIFFHeader::RIFFHeader() void RIFFParser::RIFFHeader::Reset() { + memset(mRaw, 0, sizeof(mRaw)); mPos = 0; } @@ -729,6 +738,7 @@ HeaderParser::ChunkHeader::ChunkHeader() void HeaderParser::ChunkHeader::Reset() { + memset(mRaw, 0, sizeof(mRaw)); mPos = 0; } @@ -803,6 +813,7 @@ FormatParser::FormatChunk::FormatChunk() void FormatParser::FormatChunk::Reset() { + memset(mRaw, 0, sizeof(mRaw)); mPos = 0; } diff --git a/dom/media/webaudio/AlignedTArray.h b/dom/media/webaudio/AlignedTArray.h index 2f6445882e..5fcc6824c3 100644 --- a/dom/media/webaudio/AlignedTArray.h +++ b/dom/media/webaudio/AlignedTArray.h @@ -15,7 +15,7 @@ * N: N bytes alignment for the first element, defaults to 32 */ template -class AlignedTArray : public nsTArray_Impl +class AlignedTArray : private nsTArray_Impl { static_assert((N & (N-1)) == 0, "N must be power of 2"); typedef nsTArray_Impl base_type; @@ -46,6 +46,9 @@ public: return base_type::Length() <= sExtra ? 0 : base_type::Length() - sExtra; } + using base_type::ShallowSizeOfExcludingThis; + using base_type::ShallowSizeOfIncludingThis; + private: AlignedTArray(const AlignedTArray& other) = delete; void operator=(const AlignedTArray& other) = delete; diff --git a/dom/media/webaudio/AlignmentUtils.h b/dom/media/webaudio/AlignmentUtils.h new file mode 100644 index 0000000000..6b145a8ca6 --- /dev/null +++ b/dom/media/webaudio/AlignmentUtils.h @@ -0,0 +1,29 @@ +/* -*- 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 AlignmentUtils_h__ +#define AlignmentUtils_h__ + +#define IS_ALIGNED16(ptr) ((((uintptr_t)ptr + 15) & ~0x0F) == (uintptr_t)ptr) + +#ifdef DEBUG + #define ASSERT_ALIGNED16(ptr) \ + MOZ_ASSERT(IS_ALIGNED16(ptr), \ + #ptr " has to be aligned to a 16 byte boundary"); +#else + #define ASSERT_ALIGNED16(ptr) +#endif + +#ifdef DEBUG + #define ASSERT_MULTIPLE16(v) \ + MOZ_ASSERT(v % 16 == 0, #v " has to be a a multiple of 16"); +#else + #define ASSERT_MULTIPLE16(v) +#endif + +#define ALIGNED16(ptr) (float*)(((uintptr_t)ptr + 15) & ~0x0F); + +#endif diff --git a/dom/media/webaudio/AnalyserNode.cpp b/dom/media/webaudio/AnalyserNode.cpp index 9573751f54..40fbce0ed1 100644 --- a/dom/media/webaudio/AnalyserNode.cpp +++ b/dom/media/webaudio/AnalyserNode.cpp @@ -69,15 +69,15 @@ public: if (aInput.IsNull()) { // If AnalyserNode::mChunks has only null chunks, then there is no need // to send further null chunks. - if (mChunksToProcess <= 0) { - if (mChunksToProcess != INT32_MIN) { - mChunksToProcess = INT32_MIN; - aStream->ScheduleCheckForInactive(); - } + if (mChunksToProcess == 0) { return; } --mChunksToProcess; + if (mChunksToProcess == 0) { + aStream->ScheduleCheckForInactive(); + } + } else { // This many null chunks will be required to empty AnalyserNode::mChunks. mChunksToProcess = CHUNK_COUNT; @@ -90,7 +90,7 @@ public: virtual bool IsActive() const override { - return mChunksToProcess != INT32_MIN; + return mChunksToProcess != 0; } virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override @@ -98,7 +98,7 @@ public: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } - int32_t mChunksToProcess = INT32_MIN; + uint32_t mChunksToProcess = 0; }; AnalyserNode::AnalyserNode(AudioContext* aContext) diff --git a/dom/media/webaudio/AudioBlock.cpp b/dom/media/webaudio/AudioBlock.cpp index 064235b092..a8c714019f 100644 --- a/dom/media/webaudio/AudioBlock.cpp +++ b/dom/media/webaudio/AudioBlock.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioBlock.h" +#include "AlignmentUtils.h" namespace mozilla { @@ -17,9 +18,7 @@ namespace mozilla { * buffer can reuse and modify its contents next iteration if other references * are all downstream temporary references held by AudioBlock. * - * This only guarantees 4-byte alignment of the data. For alignment we simply - * assume that the memory from malloc is at least 4-byte aligned and that - * AudioBlockBuffer's size is divisible by 4. + * We guarantee 16 byte alignment of the channel data. */ class AudioBlockBuffer final : public ThreadSharedObject { public: @@ -28,7 +27,9 @@ public: float* ChannelData(uint32_t aChannel) { - return reinterpret_cast(this + 1) + aChannel * WEBAUDIO_BLOCK_SIZE; + float* base = reinterpret_cast(((uintptr_t)(this + 1) + 15) & ~0x0F); + ASSERT_ALIGNED16(base); + return base + aChannel * WEBAUDIO_BLOCK_SIZE; } static already_AddRefed Create(uint32_t aChannelCount) @@ -37,9 +38,11 @@ public: size *= aChannelCount; size *= sizeof(float); size += sizeof(AudioBlockBuffer); + size += 15; //padding for alignment if (!size.isValid()) { MOZ_CRASH(); } + void* m = moz_xmalloc(size.value()); RefPtr p = new (m) AudioBlockBuffer(); NS_ASSERTION((reinterpret_cast(p.get() + 1) - reinterpret_cast(p.get())) % 4 == 0, @@ -124,9 +127,12 @@ AudioBlock::ClearDownstreamMark() { } } -void -AudioBlock::AssertNoLastingShares() { - MOZ_ASSERT(!mBuffer->AsAudioBlockBuffer()->HasLastingShares()); +bool +AudioBlock::CanWrite() { + // If mBufferIsDownstreamRef is set then the buffer is not ours to use. + // It may be in use by another node which is not downstream. + return !mBufferIsDownstreamRef && + !mBuffer->AsAudioBlockBuffer()->HasLastingShares(); } void @@ -147,8 +153,6 @@ AudioBlock::AllocateChannels(uint32_t aChannelCount) } } - // XXX for SIMD purposes we should do something here to make sure the - // channel buffers are 16-byte aligned. RefPtr buffer = AudioBlockBuffer::Create(aChannelCount); mChannelData.SetLength(aChannelCount); for (uint32_t i = 0; i < aChannelCount; ++i) { diff --git a/dom/media/webaudio/AudioBlock.h b/dom/media/webaudio/AudioBlock.h index 5ad2b1703c..c9a5bb4009 100644 --- a/dom/media/webaudio/AudioBlock.h +++ b/dom/media/webaudio/AudioBlock.h @@ -23,10 +23,18 @@ class AudioBlock : private AudioChunk public: AudioBlock() { mDuration = WEBAUDIO_BLOCK_SIZE; + mBufferFormat = AUDIO_FORMAT_SILENCE; } - AudioBlock(const AudioChunk& aChunk) { - mDuration = WEBAUDIO_BLOCK_SIZE; - operator=(aChunk); + // No effort is made in constructors to ensure that mBufferIsDownstreamRef + // is set because the block is expected to be a temporary and so the + // reference will be released before the next iteration. + // The custom copy constructor is required so as not to set + // mBufferIsDownstreamRef without notifying AudioBlockBuffer. + AudioBlock(const AudioBlock& aBlock) : AudioChunk(aBlock.AsAudioChunk()) {} + explicit AudioBlock(const AudioChunk& aChunk) + : AudioChunk(aChunk) + { + MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE); } ~AudioBlock(); @@ -44,7 +52,7 @@ public: const AudioChunk& AsAudioChunk() const { return *this; } AudioChunk* AsMutableChunk() { - void ClearDownstreamMark(); + ClearDownstreamMark(); return this; } @@ -54,12 +62,14 @@ public: */ void AllocateChannels(uint32_t aChannelCount); + /** + * ChannelFloatsForWrite() should only be used when the buffers have been + * created with AllocateChannels(). + */ float* ChannelFloatsForWrite(size_t aChannel) { MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32); -#if DEBUG - AssertNoLastingShares(); -#endif + MOZ_ASSERT(CanWrite()); return static_cast(const_cast(mChannelData[aChannel])); } @@ -72,6 +82,12 @@ public: mBufferFormat = AUDIO_FORMAT_SILENCE; } + AudioBlock& operator=(const AudioBlock& aBlock) { + // Instead of just copying, mBufferIsDownstreamRef must be first cleared + // if set. It is set again for the new mBuffer if possible. This happens + // in SetBuffer(). + return *this = aBlock.AsAudioChunk(); + } AudioBlock& operator=(const AudioChunk& aChunk) { MOZ_ASSERT(aChunk.mDuration == WEBAUDIO_BLOCK_SIZE); SetBuffer(aChunk.mBuffer); @@ -103,7 +119,7 @@ public: private: void ClearDownstreamMark(); - void AssertNoLastingShares(); + bool CanWrite(); // mBufferIsDownstreamRef is set only when mBuffer references an // AudioBlockBuffer created in a different AudioBlock. That can happen when diff --git a/dom/media/webaudio/AudioBufferSourceNode.cpp b/dom/media/webaudio/AudioBufferSourceNode.cpp index 87a6ecc4f8..4fc3cd593a 100644 --- a/dom/media/webaudio/AudioBufferSourceNode.cpp +++ b/dom/media/webaudio/AudioBufferSourceNode.cpp @@ -10,6 +10,7 @@ #include "mozilla/FloatingPoint.h" #include "nsContentUtils.h" #include "nsMathUtils.h" +#include "AlignmentUtils.h" #include "AudioNodeEngine.h" #include "AudioNodeStream.h" #include "AudioDestinationNode.h" @@ -410,7 +411,15 @@ public: uint32_t numFrames = std::min(aBufferMax - mBufferPosition, availableInOutput); - if (numFrames == WEBAUDIO_BLOCK_SIZE) { + + bool inputBufferAligned = true; + for (uint32_t i = 0; i < aChannels; ++i) { + if (!IS_ALIGNED16(mBuffer->GetData(i) + mBufferPosition)) { + inputBufferAligned = false; + } + } + + if (numFrames == WEBAUDIO_BLOCK_SIZE && inputBufferAligned) { MOZ_ASSERT(mBufferPosition < aBufferMax); BorrowFromInputBuffer(aOutput, aChannels); } else { @@ -661,6 +670,10 @@ AudioBufferSourceNode::Start(double aWhen, double aOffset, mOffset = aOffset; mDuration = aDuration.WasPassed() ? aDuration.Value() : std::numeric_limits::min(); + + WEB_AUDIO_API_LOG("%f: %s %u Start(%f, %g, %g)", Context()->CurrentTime(), + NodeType(), Id(), aWhen, aOffset, mDuration); + // We can't send these parameters without a buffer because we don't know the // buffer's sample rate or length. if (mBuffer) { @@ -742,6 +755,9 @@ AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv) return; } + WEB_AUDIO_API_LOG("%f: %s %u Stop(%f)", Context()->CurrentTime(), + NodeType(), Id(), aWhen); + AudioNodeStream* ns = mStream; if (!ns || !Context()) { // We've already stopped and had our stream shut down diff --git a/dom/media/webaudio/AudioContext.cpp b/dom/media/webaudio/AudioContext.cpp index 029a54c7ce..0fed87d8de 100644 --- a/dom/media/webaudio/AudioContext.cpp +++ b/dom/media/webaudio/AudioContext.cpp @@ -62,6 +62,9 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioContext) if (!tmp->mIsStarted) { NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveNodes) } + // Remove weak reference on the global window as the context is not usable + // without mDestination. + tmp->DisconnectFromWindow(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(DOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(AudioContext, @@ -77,7 +80,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_ADDREF_INHERITED(AudioContext, DOMEventTargetHelper) NS_IMPL_RELEASE_INHERITED(AudioContext, DOMEventTargetHelper) + NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioContext) + NS_INTERFACE_MAP_ENTRY(nsIMemoryReporter) NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) static float GetSampleRateForAudioContext(bool aIsOffline, float aSampleRate) @@ -123,27 +128,28 @@ AudioContext::AudioContext(nsPIDOMWindow* aWindow, nsresult AudioContext::Init() { - // We skip calling SetIsOnlyNodeForContext and the creation of the - // audioChannelAgent during mDestination's constructor, because we can only - // call them after mDestination has been set up. if (!mIsOffline) { nsresult rv = mDestination->CreateAudioChannelAgent(); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - mDestination->SetIsOnlyNodeForContext(true); } return NS_OK; } -AudioContext::~AudioContext() +void +AudioContext::DisconnectFromWindow() { nsPIDOMWindow* window = GetOwner(); if (window) { window->RemoveAudioContext(this); } - +} + +AudioContext::~AudioContext() +{ + DisconnectFromWindow(); UnregisterWeakMemoryReporter(this); } @@ -528,7 +534,8 @@ AudioContext::CreatePeriodicWave(const Float32Array& aRealData, RefPtr periodicWave = new PeriodicWave(this, aRealData.Data(), aImagData.Data(), - aImagData.Length(), aRv); + aImagData.Length(), aConstraints.mDisableNormalization, + aRv); if (aRv.Failed()) { return nullptr; } @@ -667,8 +674,7 @@ double AudioContext::CurrentTime() const { MediaStream* stream = Destination()->Stream(); - return stream->StreamTimeToSeconds(stream->GetCurrentTime() + - Destination()->ExtraCurrentTime()); + return stream->StreamTimeToSeconds(stream->GetCurrentTime()); } void @@ -978,13 +984,6 @@ AudioContext::RegisterNode(AudioNode* aNode) { MOZ_ASSERT(!mAllNodes.Contains(aNode)); mAllNodes.PutEntry(aNode); - // mDestinationNode may be null when we're destroying nodes unlinked by CC. - // Skipping unnecessary calls after shutdown avoids RunInStableState events - // getting stuck in CycleCollectedJSRuntime during final cycle collection - // (bug 1200514). - if (mDestination && !mIsShutDown) { - mDestination->SetIsOnlyNodeForContext(mAllNodes.Count() == 1); - } } void @@ -992,10 +991,6 @@ AudioContext::UnregisterNode(AudioNode* aNode) { MOZ_ASSERT(mAllNodes.Contains(aNode)); mAllNodes.RemoveEntry(aNode); - // mDestinationNode may be null when we're destroying nodes unlinked by CC - if (mDestination) { - mDestination->SetIsOnlyNodeForContext(mAllNodes.Count() == 1); - } } JSObject* @@ -1023,6 +1018,9 @@ AudioContext::StartRendering(ErrorResult& aRv) mIsStarted = true; RefPtr promise = Promise::Create(parentObject, aRv); + if (aRv.Failed()) { + return nullptr; + } mDestination->StartRendering(promise); OnStateChanged(nullptr, AudioContextState::Running); @@ -1030,6 +1028,13 @@ AudioContext::StartRendering(ErrorResult& aRv) return promise.forget(); } +unsigned long +AudioContext::Length() +{ + MOZ_ASSERT(mIsOffline); + return mDestination->Length(); +} + void AudioContext::Mute() const { diff --git a/dom/media/webaudio/AudioContext.h b/dom/media/webaudio/AudioContext.h index e5cb4b7d0b..619b122ade 100644 --- a/dom/media/webaudio/AudioContext.h +++ b/dom/media/webaudio/AudioContext.h @@ -78,7 +78,7 @@ enum class OscillatorType : uint32_t; class BasicWaveFormCache { public: - BasicWaveFormCache(uint32_t aSampleRate); + explicit BasicWaveFormCache(uint32_t aSampleRate); NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BasicWaveFormCache) WebCore::PeriodicWave* GetBasicWaveForm(OscillatorType aType); private: @@ -268,6 +268,7 @@ public: // OfflineAudioContext methods already_AddRefed StartRendering(ErrorResult& aRv); IMPL_EVENT_HANDLER(complete) + unsigned long Length(); bool IsOffline() const { return mIsOffline; } @@ -313,6 +314,7 @@ public: IMPL_EVENT_HANDLER(mozinterruptend) private: + void DisconnectFromWindow(); void RemoveFromDecodeQueue(WebAudioDecodeJob* aDecodeJob); void ShutdownDecoder(); diff --git a/dom/media/webaudio/AudioDestinationNode.cpp b/dom/media/webaudio/AudioDestinationNode.cpp index 82a69d5e89..978f922b0b 100644 --- a/dom/media/webaudio/AudioDestinationNode.cpp +++ b/dom/media/webaudio/AudioDestinationNode.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "AudioDestinationNode.h" +#include "AlignmentUtils.h" #include "AudioContext.h" #include "mozilla/dom/AudioDestinationNodeBinding.h" #include "mozilla/dom/ScriptSettings.h" @@ -55,18 +56,26 @@ public: // will not go anywhere. *aOutput = aInput; - // The output buffer is allocated lazily, on the rendering thread. - if (!mBufferAllocated) { + // The output buffer is allocated lazily, on the rendering thread, when + // non-null input is received. + if (!mBufferAllocated && !aInput.IsNull()) { // These allocations might fail if content provides a huge number of // channels or size, but it's OK since we'll deal with the failure // gracefully. mBuffer = ThreadSharedFloatArrayBufferList:: Create(mNumberOfChannels, mLength, fallible); + if (mBuffer && mWriteIndex) { + // Zero leading for any null chunks that were skipped. + for (uint32_t i = 0; i < mNumberOfChannels; ++i) { + float* channelData = mBuffer->GetDataForWrite(i); + PodZero(channelData, mWriteIndex); + } + } mBufferAllocated = true; } - // Handle the case of allocation failure in the input buffer + // Skip copying if there is no buffer. uint32_t outputChannelCount = mBuffer ? mNumberOfChannels : 0; // Record our input buffer @@ -79,7 +88,7 @@ public: PodZero(outputData, duration); } else { const float* inputBuffer = static_cast(aInput.mChannelData[i]); - if (duration == WEBAUDIO_BLOCK_SIZE) { + if (duration == WEBAUDIO_BLOCK_SIZE && IS_ALIGNED16(inputBuffer)) { // Use the optimized version of the copy with scale operation AudioBlockCopyChannelWithScale(inputBuffer, aInput.mVolume, outputData); @@ -323,8 +332,6 @@ AudioDestinationNode::AudioDestinationNode(AudioContext* aContext, , mAudioChannel(AudioChannel::Normal) , mIsOffline(aIsOffline) , mAudioChannelAgentPlaying(false) - , mExtraCurrentTimeSinceLastStartedBlocking(0) - , mExtraCurrentTimeUpdatedSinceLastStableState(false) , mCaptured(false) { MediaStreamGraph* graph = aIsOffline ? @@ -640,74 +647,6 @@ AudioDestinationNode::CreateAudioChannelAgent() return NS_OK; } -void -AudioDestinationNode::NotifyStableState() -{ - mExtraCurrentTimeUpdatedSinceLastStableState = false; -} - -void -AudioDestinationNode::ScheduleStableStateNotification() -{ - nsCOMPtr event = - NS_NewRunnableMethod(this, &AudioDestinationNode::NotifyStableState); - // Dispatch will fail if this is called on AudioNode destruction during - // shutdown, in which case failure can be ignored. - nsContentUtils::RunInStableState(event.forget()); -} - -StreamTime -AudioDestinationNode::ExtraCurrentTime() -{ - if (!mStartedBlockingDueToBeingOnlyNode.IsNull() && - !mExtraCurrentTimeUpdatedSinceLastStableState) { - mExtraCurrentTimeUpdatedSinceLastStableState = true; - // Round to nearest processing block. - double seconds = - (TimeStamp::Now() - mStartedBlockingDueToBeingOnlyNode).ToSeconds(); - mExtraCurrentTimeSinceLastStartedBlocking = WEBAUDIO_BLOCK_SIZE * - StreamTime(seconds * Context()->SampleRate() / WEBAUDIO_BLOCK_SIZE + 0.5); - ScheduleStableStateNotification(); - } - return mExtraCurrentTimeSinceLastStartedBlocking; -} - -void -AudioDestinationNode::SetIsOnlyNodeForContext(bool aIsOnlyNode) -{ - if (!mStartedBlockingDueToBeingOnlyNode.IsNull() == aIsOnlyNode) { - // Nothing changed. - return; - } - - if (!mStream) { - // DestroyMediaStream has been called, presumably during CC Unlink(). - return; - } - - if (mIsOffline) { - // Don't block the destination stream for offline AudioContexts, since - // we expect the zero data produced when there are no other nodes to - // show up in its result buffer. Also, we would get confused by adding - // ExtraCurrentTime before StartRendering has even been called. - return; - } - - if (aIsOnlyNode) { - mStream->Suspend(); - mStartedBlockingDueToBeingOnlyNode = TimeStamp::Now(); - // Don't do an update of mExtraCurrentTimeSinceLastStartedBlocking until the next stable state. - mExtraCurrentTimeUpdatedSinceLastStableState = true; - ScheduleStableStateNotification(); - } else { - // Force update of mExtraCurrentTimeSinceLastStartedBlocking if necessary - ExtraCurrentTime(); - mStream->AdvanceAndResume(mExtraCurrentTimeSinceLastStartedBlocking); - mExtraCurrentTimeSinceLastStartedBlocking = 0; - mStartedBlockingDueToBeingOnlyNode = TimeStamp(); - } -} - void AudioDestinationNode::InputMuted(bool aMuted) { diff --git a/dom/media/webaudio/AudioDestinationNode.h b/dom/media/webaudio/AudioDestinationNode.h index 96a14e4355..708cd1cc6e 100644 --- a/dom/media/webaudio/AudioDestinationNode.h +++ b/dom/media/webaudio/AudioDestinationNode.h @@ -65,13 +65,6 @@ public: void NotifyMainThreadStreamFinished() override; void FireOfflineCompletionEvent(); - // An amount that should be added to the MediaStream's current time to - // get the AudioContext.currentTime. - StreamTime ExtraCurrentTime(); - - // When aIsOnlyNode is true, this is the only node for the AudioContext. - void SetIsOnlyNodeForContext(bool aIsOnlyNode); - nsresult CreateAudioChannelAgent(); void DestroyAudioChannelAgent(); @@ -86,6 +79,12 @@ public: void InputMuted(bool aInputMuted); void ResolvePromise(AudioBuffer* aRenderedBuffer); + unsigned long Length() + { + MOZ_ASSERT(mIsOffline); + return mFramesToProduce; + } + protected: virtual ~AudioDestinationNode(); @@ -95,9 +94,6 @@ private: void SetCanPlay(float aVolume, bool aMuted); - void NotifyStableState(); - void ScheduleStableStateNotification(); - SelfReference mOfflineRenderingRef; uint32_t mFramesToProduce; @@ -111,9 +107,6 @@ private: bool mIsOffline; bool mAudioChannelAgentPlaying; - TimeStamp mStartedBlockingDueToBeingOnlyNode; - StreamTime mExtraCurrentTimeSinceLastStartedBlocking; - bool mExtraCurrentTimeUpdatedSinceLastStableState; bool mCaptured; }; diff --git a/dom/media/webaudio/AudioEventTimeline.h b/dom/media/webaudio/AudioEventTimeline.h index 2ca9310d01..7be7d78fe0 100644 --- a/dom/media/webaudio/AudioEventTimeline.h +++ b/dom/media/webaudio/AudioEventTimeline.h @@ -38,9 +38,10 @@ struct AudioTimelineEvent final }; AudioTimelineEvent(Type aType, double aTime, float aValue, double aTimeConstant = 0.0, - float aDuration = 0.0, const float* aCurve = nullptr, + double aDuration = 0.0, const float* aCurve = nullptr, uint32_t aCurveLength = 0) : mType(aType) + , mCurve(nullptr) , mTimeConstant(aTimeConstant) , mDuration(aDuration) #ifdef DEBUG @@ -57,7 +58,10 @@ struct AudioTimelineEvent final explicit AudioTimelineEvent(MediaStream* aStream) : mType(Stream) + , mCurve(nullptr) , mStream(aStream) + , mTimeConstant(0.0) + , mDuration(0.0) #ifdef DEBUG , mTimeIsInTicks(false) #endif @@ -209,6 +213,8 @@ public: // curve event. for (unsigned i = 0; i < mEvents.Length(); ++i) { if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve && + !(aEvent.mType == AudioTimelineEvent::SetValueCurve && + aEvent.template Time() == mEvents[i].template Time()) && mEvents[i].template Time() <= aEvent.template Time() && (mEvents[i].template Time() + mEvents[i].mDuration) >= aEvent.template Time()) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); @@ -220,6 +226,11 @@ public: // events. if (aEvent.mType == AudioTimelineEvent::SetValueCurve) { for (unsigned i = 0; i < mEvents.Length(); ++i) { + // In case we have two curve at the same time + if (mEvents[i].mType == AudioTimelineEvent::SetValueCurve && + mEvents[i].template Time() == aEvent.template Time()) { + continue; + } if (mEvents[i].template Time() > aEvent.template Time() && mEvents[i].template Time() < (aEvent.template Time() + aEvent.mDuration)) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); diff --git a/dom/media/webaudio/AudioNode.cpp b/dom/media/webaudio/AudioNode.cpp index 23bc8b0f9d..10b9cc1c4d 100644 --- a/dom/media/webaudio/AudioNode.cpp +++ b/dom/media/webaudio/AudioNode.cpp @@ -193,6 +193,10 @@ AudioNode::Connect(AudioNode& aDestination, uint32_t aOutput, return &aDestination; } + WEB_AUDIO_API_LOG("%f: %s %u Connect() to %s %u", + Context()->CurrentTime(), NodeType(), Id(), + aDestination.NodeType(), aDestination.Id()); + // The MediaStreamGraph will handle cycle detection. We don't need to do it // here. @@ -295,6 +299,9 @@ AudioNode::Disconnect(uint32_t aOutput, ErrorResult& aRv) return; } + WEB_AUDIO_API_LOG("%f: %s %u Disconnect()", Context()->CurrentTime(), + NodeType(), Id()); + // An upstream node may be starting to play on the graph thread, and the // engine for a downstream node may be sending a PlayingRefChangeHandler // ADDREF message to this (main) thread. Wait for a round trip before diff --git a/dom/media/webaudio/AudioNodeEngine.cpp b/dom/media/webaudio/AudioNodeEngine.cpp index e950aafc8d..f720c209bd 100644 --- a/dom/media/webaudio/AudioNodeEngine.cpp +++ b/dom/media/webaudio/AudioNodeEngine.cpp @@ -9,6 +9,11 @@ #include "mozilla/arm.h" #include "AudioNodeEngineNEON.h" #endif +#ifdef USE_SSE2 +#include "mozilla/SSE.h" +#include "AlignmentUtils.h" +#include "AudioNodeEngineSSE2.h" +#endif namespace mozilla { @@ -71,6 +76,14 @@ void AudioBufferAddWithScale(const float* aInput, return; } #endif + +#ifdef USE_SSE2 + if (mozilla::supports_sse2()) { + AudioBufferAddWithScale_SSE(aInput, aScale, aOutput, aSize); + return; + } +#endif + if (aScale == 1.0f) { for (uint32_t i = 0; i < aSize; ++i) { aOutput[i] += aInput[i]; @@ -104,6 +117,14 @@ AudioBlockCopyChannelWithScale(const float* aInput, return; } #endif + +#ifdef USE_SSE2 + if (mozilla::supports_sse2()) { + AudioBlockCopyChannelWithScale_SSE(aInput, aScale, aOutput); + return; + } +#endif + for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { aOutput[i] = aInput[i]*aScale; } @@ -116,6 +137,14 @@ BufferComplexMultiply(const float* aInput, float* aOutput, uint32_t aSize) { + +#ifdef USE_SSE2 + if (mozilla::supports_sse()) { + BufferComplexMultiply_SSE(aInput, aScale, aOutput, aSize); + return; + } +#endif + for (uint32_t i = 0; i < aSize * 2; i += 2) { float real1 = aInput[i]; float imag1 = aInput[i + 1]; @@ -152,6 +181,14 @@ AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE], return; } #endif + +#ifdef USE_SSE2 + if (mozilla::supports_sse2()) { + AudioBlockCopyChannelWithScale_SSE(aInput, aScale, aOutput); + return; + } +#endif + for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) { aOutput[i] = aInput[i]*aScale[i]; } @@ -178,6 +215,14 @@ AudioBufferInPlaceScale(float* aBlock, return; } #endif + +#ifdef USE_SSE2 + if (mozilla::supports_sse2()) { + AudioBufferInPlaceScale_SSE(aBlock, aScale, aSize); + return; + } +#endif + for (uint32_t i = 0; i < aSize; ++i) { *aBlock++ *= aScale; } @@ -220,6 +265,15 @@ AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE], } #endif +#ifdef USE_SSE2 + if (mozilla::supports_sse2()) { + AudioBlockPanStereoToStereo_SSE(aInputL, aInputR, + aGainL, aGainR, aIsOnTheLeft, + aOutputL, aOutputR); + return; + } +#endif + uint32_t i; if (aIsOnTheLeft) { @@ -245,7 +299,12 @@ AudioBlockPanStereoToStereo(const float aInputL[WEBAUDIO_BLOCK_SIZE], float aOutputR[WEBAUDIO_BLOCK_SIZE]) { #ifdef BUILD_ARM_NEON - // No NEON version yet: bug 1105513 + if (mozilla::supports_neon()) { + AudioBlockPanStereoToStereo_NEON(aInputL, aInputR, + aGainL, aGainR, aIsOnTheLeft, + aOutputL, aOutputR); + return; + } #endif uint32_t i; @@ -264,6 +323,27 @@ float AudioBufferSumOfSquares(const float* aInput, uint32_t aLength) { float sum = 0.0f; + +#ifdef USE_SSE2 + if (mozilla::supports_sse()) { + const float* alignedInput = ALIGNED16(aInput); + float vLength = (aLength >> 4) << 4; + + // use scalar operations for any unaligned data at the beginning + while (aInput != alignedInput) { + sum += *aInput * *aInput; + ++aInput; + } + + sum += AudioBufferSumOfSquares_SSE(alignedInput, vLength); + + // adjust aInput and aLength to use scalar operations for any + // remaining values + aInput = alignedInput + 1; + aLength -= vLength; + } +#endif + while (aLength--) { sum += *aInput * *aInput; ++aInput; diff --git a/dom/media/webaudio/AudioNodeEngine.h b/dom/media/webaudio/AudioNodeEngine.h index 73a14956d9..58a9f96212 100644 --- a/dom/media/webaudio/AudioNodeEngine.h +++ b/dom/media/webaudio/AudioNodeEngine.h @@ -394,7 +394,7 @@ public: AudioNodeSizes& aUsage) const { aUsage.mEngine = SizeOfIncludingThis(aMallocSizeOf); - if (HasNode()) { + if (mNode) { aUsage.mDomNode = mNode->SizeOfIncludingThis(aMallocSizeOf); aUsage.mNodeType = mNode->NodeType(); } diff --git a/dom/media/webaudio/AudioNodeEngineNEON.cpp b/dom/media/webaudio/AudioNodeEngineNEON.cpp index b92975083b..079a1cc8bc 100644 --- a/dom/media/webaudio/AudioNodeEngineNEON.cpp +++ b/dom/media/webaudio/AudioNodeEngineNEON.cpp @@ -222,4 +222,97 @@ AudioBlockPanStereoToStereo_NEON(const float aInputL[WEBAUDIO_BLOCK_SIZE], } } } + +void +AudioBlockPanStereoToStereo_NEON(const float aInputL[WEBAUDIO_BLOCK_SIZE], + const float aInputR[WEBAUDIO_BLOCK_SIZE], + float aGainL[WEBAUDIO_BLOCK_SIZE], + float aGainR[WEBAUDIO_BLOCK_SIZE], + const bool aIsOnTheLeft[WEBAUDIO_BLOCK_SIZE], + float aOutputL[WEBAUDIO_BLOCK_SIZE], + float aOutputR[WEBAUDIO_BLOCK_SIZE]) +{ + ASSERT_ALIGNED(aInputL); + ASSERT_ALIGNED(aInputR); + ASSERT_ALIGNED(aGainL); + ASSERT_ALIGNED(aGainR); + ASSERT_ALIGNED(aIsOnTheLeft); + ASSERT_ALIGNED(aOutputL); + ASSERT_ALIGNED(aOutputR); + + float32x4_t vinL0, vinL1; + float32x4_t vinR0, vinR1; + float32x4_t voutL0, voutL1; + float32x4_t voutR0, voutR1; + float32x4_t vscaleL0, vscaleL1; + float32x4_t vscaleR0, vscaleR1; + float32x4_t onleft0, onleft1, notonleft0, notonleft1; + + float32x4_t zero = {0, 0, 0, 0}; + uint8x8_t isOnTheLeft; + + for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; i+=8) { + vinL0 = vld1q_f32(ADDRESS_OF(aInputL, i)); + vinL1 = vld1q_f32(ADDRESS_OF(aInputL, i+4)); + + vinR0 = vld1q_f32(ADDRESS_OF(aInputR, i)); + vinR1 = vld1q_f32(ADDRESS_OF(aInputR, i+4)); + + vscaleL0 = vld1q_f32(ADDRESS_OF(aGainL, i)); + vscaleL1 = vld1q_f32(ADDRESS_OF(aGainL, i+4)); + + vscaleR0 = vld1q_f32(ADDRESS_OF(aGainR, i)); + vscaleR1 = vld1q_f32(ADDRESS_OF(aGainR, i+4)); + + // Load output with boolean "on the left" values. This assumes that + // bools are stored as a single byte. + isOnTheLeft = vld1_u8((uint8_t *)&aIsOnTheLeft[i]); + voutL0 = vsetq_lane_f32(vget_lane_u8(isOnTheLeft, 0), voutL0, 0); + voutL0 = vsetq_lane_f32(vget_lane_u8(isOnTheLeft, 1), voutL0, 1); + voutL0 = vsetq_lane_f32(vget_lane_u8(isOnTheLeft, 2), voutL0, 2); + voutL0 = vsetq_lane_f32(vget_lane_u8(isOnTheLeft, 3), voutL0, 3); + voutL1 = vsetq_lane_f32(vget_lane_u8(isOnTheLeft, 4), voutL1, 0); + voutL1 = vsetq_lane_f32(vget_lane_u8(isOnTheLeft, 5), voutL1, 1); + voutL1 = vsetq_lane_f32(vget_lane_u8(isOnTheLeft, 6), voutL1, 2); + voutL1 = vsetq_lane_f32(vget_lane_u8(isOnTheLeft, 7), voutL1, 3); + + // Convert the boolean values into masks by setting all bits to 1 + // if true. + voutL0 = (float32x4_t)vcgtq_f32(voutL0, zero); + voutL1 = (float32x4_t)vcgtq_f32(voutL1, zero); + + // The right output masks are the same as the left masks + voutR0 = voutL0; + voutR1 = voutL1; + + // Calculate left channel assuming isOnTheLeft + onleft0 = vmlaq_f32(vinL0, vinR0, vscaleL0); + onleft1 = vmlaq_f32(vinL1, vinR1, vscaleL0); + + // Calculate left channel assuming not isOnTheLeft + notonleft0 = vmulq_f32(vinL0, vscaleL0); + notonleft1 = vmulq_f32(vinL1, vscaleL1); + + // Write results using previously stored masks + voutL0 = vbslq_f32((uint32x4_t)voutL0, onleft0, notonleft0); + voutL1 = vbslq_f32((uint32x4_t)voutL1, onleft1, notonleft1); + + // Calculate right channel assuming isOnTheLeft + onleft0 = vmulq_f32(vinR0, vscaleR0); + onleft1 = vmulq_f32(vinR1, vscaleR1); + + // Calculate right channel assuming not isOnTheLeft + notonleft0 = vmlaq_f32(vinR0, vinL0, vscaleR0); + notonleft1 = vmlaq_f32(vinR1, vinL1, vscaleR1); + + // Write results using previously stored masks + voutR0 = vbslq_f32((uint32x4_t)voutR0, onleft0, notonleft0); + voutR1 = vbslq_f32((uint32x4_t)voutR1, onleft1, notonleft1); + + vst1q_f32(ADDRESS_OF(aOutputL, i), voutL0); + vst1q_f32(ADDRESS_OF(aOutputL, i+4), voutL1); + vst1q_f32(ADDRESS_OF(aOutputR, i), voutR0); + vst1q_f32(ADDRESS_OF(aOutputR, i+4), voutR1); + } +} } diff --git a/dom/media/webaudio/AudioNodeEngineNEON.h b/dom/media/webaudio/AudioNodeEngineNEON.h index 334f7d62e0..2b3e89b751 100644 --- a/dom/media/webaudio/AudioNodeEngineNEON.h +++ b/dom/media/webaudio/AudioNodeEngineNEON.h @@ -35,6 +35,15 @@ AudioBlockPanStereoToStereo_NEON(const float aInputL[WEBAUDIO_BLOCK_SIZE], float aGainL, float aGainR, bool aIsOnTheLeft, float aOutputL[WEBAUDIO_BLOCK_SIZE], float aOutputR[WEBAUDIO_BLOCK_SIZE]); + +void +AudioBlockPanStereoToStereo_NEON(const float aInputL[WEBAUDIO_BLOCK_SIZE], + const float aInputR[WEBAUDIO_BLOCK_SIZE], + float aGainL[WEBAUDIO_BLOCK_SIZE], + float aGainR[WEBAUDIO_BLOCK_SIZE], + const bool aIsOnTheLeft[WEBAUDIO_BLOCK_SIZE], + float aOutputL[WEBAUDIO_BLOCK_SIZE], + float aOutputR[WEBAUDIO_BLOCK_SIZE]); } #endif /* MOZILLA_AUDIONODEENGINENEON_H_ */ diff --git a/dom/media/webaudio/AudioNodeEngineSSE2.cpp b/dom/media/webaudio/AudioNodeEngineSSE2.cpp new file mode 100644 index 0000000000..a033232390 --- /dev/null +++ b/dom/media/webaudio/AudioNodeEngineSSE2.cpp @@ -0,0 +1,315 @@ +/* -*- 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 "AudioNodeEngineSSE2.h" +#include "AlignmentUtils.h" +#include + + +namespace mozilla { +void +AudioBufferAddWithScale_SSE(const float* aInput, + float aScale, + float* aOutput, + uint32_t aSize) +{ + __m128 vin0, vin1, vin2, vin3, + vscaled0, vscaled1, vscaled2, vscaled3, + vout0, vout1, vout2, vout3, + vgain; + + ASSERT_ALIGNED16(aInput); + ASSERT_ALIGNED16(aOutput); + ASSERT_MULTIPLE16(aSize); + + vgain = _mm_load1_ps(&aScale); + + for (unsigned i = 0; i < aSize; i+=16) { + vin0 = _mm_load_ps(&aInput[i]); + vin1 = _mm_load_ps(&aInput[i + 4]); + vin2 = _mm_load_ps(&aInput[i + 8]); + vin3 = _mm_load_ps(&aInput[i + 12]); + + vscaled0 = _mm_mul_ps(vin0, vgain); + vscaled1 = _mm_mul_ps(vin1, vgain); + vscaled2 = _mm_mul_ps(vin2, vgain); + vscaled3 = _mm_mul_ps(vin3, vgain); + + vin0 = _mm_load_ps(&aOutput[i]); + vin1 = _mm_load_ps(&aOutput[i + 4]); + vin2 = _mm_load_ps(&aOutput[i + 8]); + vin3 = _mm_load_ps(&aOutput[i + 12]); + + vout0 = _mm_add_ps(vin0, vscaled0); + vout1 = _mm_add_ps(vin1, vscaled1); + vout2 = _mm_add_ps(vin2, vscaled2); + vout3 = _mm_add_ps(vin3, vscaled3); + + _mm_store_ps(&aOutput[i], vout0); + _mm_store_ps(&aOutput[i + 4], vout1); + _mm_store_ps(&aOutput[i + 8], vout2); + _mm_store_ps(&aOutput[i + 12], vout3); + } +} + +void +AudioBlockCopyChannelWithScale_SSE(const float* aInput, + float aScale, + float* aOutput) +{ + __m128 vin0, vin1, vin2, vin3, + vout0, vout1, vout2, vout3; + + ASSERT_ALIGNED16(aInput); + ASSERT_ALIGNED16(aOutput); + + __m128 vgain = _mm_load1_ps(&aScale); + + for (unsigned i = 0 ; i < WEBAUDIO_BLOCK_SIZE; i+=16) { + vin0 = _mm_load_ps(&aInput[i]); + vin1 = _mm_load_ps(&aInput[i + 4]); + vin2 = _mm_load_ps(&aInput[i + 8]); + vin3 = _mm_load_ps(&aInput[i + 12]); + vout0 = _mm_mul_ps(vin0, vgain); + vout1 = _mm_mul_ps(vin1, vgain); + vout2 = _mm_mul_ps(vin2, vgain); + vout3 = _mm_mul_ps(vin3, vgain); + _mm_store_ps(&aOutput[i], vout0); + _mm_store_ps(&aOutput[i + 4], vout1); + _mm_store_ps(&aOutput[i + 8], vout2); + _mm_store_ps(&aOutput[i + 12], vout3); + } +} + +void +AudioBlockCopyChannelWithScale_SSE(const float aInput[WEBAUDIO_BLOCK_SIZE], + const float aScale[WEBAUDIO_BLOCK_SIZE], + float aOutput[WEBAUDIO_BLOCK_SIZE]) +{ + __m128 vin0, vin1, vin2, vin3, + vscaled0, vscaled1, vscaled2, vscaled3, + vout0, vout1, vout2, vout3; + + ASSERT_ALIGNED16(aInput); + ASSERT_ALIGNED16(aScale); + ASSERT_ALIGNED16(aOutput); + + for (unsigned i = 0 ; i < WEBAUDIO_BLOCK_SIZE; i+=16) { + vscaled0 = _mm_load_ps(&aScale[i]); + vscaled1 = _mm_load_ps(&aScale[i+4]); + vscaled2 = _mm_load_ps(&aScale[i+8]); + vscaled3 = _mm_load_ps(&aScale[i+12]); + + vin0 = _mm_load_ps(&aInput[i]); + vin1 = _mm_load_ps(&aInput[i + 4]); + vin2 = _mm_load_ps(&aInput[i + 8]); + vin3 = _mm_load_ps(&aInput[i + 12]); + + vout0 = _mm_mul_ps(vin0, vscaled0); + vout1 = _mm_mul_ps(vin1, vscaled1); + vout2 = _mm_mul_ps(vin2, vscaled2); + vout3 = _mm_mul_ps(vin3, vscaled3); + + _mm_store_ps(&aOutput[i], vout0); + _mm_store_ps(&aOutput[i + 4], vout1); + _mm_store_ps(&aOutput[i + 8], vout2); + _mm_store_ps(&aOutput[i + 12], vout3); + } +} + +void +AudioBufferInPlaceScale_SSE(float* aBlock, + float aScale, + uint32_t aSize) +{ + __m128 vout0, vout1, vout2, vout3, + vin0, vin1, vin2, vin3; + + ASSERT_ALIGNED16(aBlock); + ASSERT_MULTIPLE16(aSize); + + __m128 vgain = _mm_load1_ps(&aScale); + + for (unsigned i = 0; i < aSize; i+=16) { + vin0 = _mm_load_ps(&aBlock[i]); + vin1 = _mm_load_ps(&aBlock[i + 4]); + vin2 = _mm_load_ps(&aBlock[i + 8]); + vin3 = _mm_load_ps(&aBlock[i + 12]); + vout0 = _mm_mul_ps(vin0, vgain); + vout1 = _mm_mul_ps(vin1, vgain); + vout2 = _mm_mul_ps(vin2, vgain); + vout3 = _mm_mul_ps(vin3, vgain); + _mm_store_ps(&aBlock[i], vout0); + _mm_store_ps(&aBlock[i + 4], vout1); + _mm_store_ps(&aBlock[i + 8], vout2); + _mm_store_ps(&aBlock[i + 12], vout3); + } +} + +void +AudioBlockPanStereoToStereo_SSE(const float aInputL[WEBAUDIO_BLOCK_SIZE], + const float aInputR[WEBAUDIO_BLOCK_SIZE], + float aGainL, float aGainR, bool aIsOnTheLeft, + float aOutputL[WEBAUDIO_BLOCK_SIZE], + float aOutputR[WEBAUDIO_BLOCK_SIZE]) +{ + __m128 vinl0, vinr0, vinl1, vinr1, + vout0, vout1, + vscaled0, vscaled1, + vgainl, vgainr; + + ASSERT_ALIGNED16(aInputL); + ASSERT_ALIGNED16(aInputR); + ASSERT_ALIGNED16(aOutputL); + ASSERT_ALIGNED16(aOutputR); + + vgainl = _mm_load1_ps(&aGainL); + vgainr = _mm_load1_ps(&aGainR); + + if (aIsOnTheLeft) { + for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; i+=8) { + vinl0 = _mm_load_ps(&aInputL[i]); + vinr0 = _mm_load_ps(&aInputR[i]); + vinl1 = _mm_load_ps(&aInputL[i+4]); + vinr1 = _mm_load_ps(&aInputR[i+4]); + + /* left channel : aOutputL = aInputL + aInputR * gainL */ + vscaled0 = _mm_mul_ps(vinr0, vgainl); + vscaled1 = _mm_mul_ps(vinr1, vgainl); + vout0 = _mm_add_ps(vscaled0, vinl0); + vout1 = _mm_add_ps(vscaled1, vinl1); + _mm_store_ps(&aOutputL[i], vout0); + _mm_store_ps(&aOutputL[i+4], vout1); + + /* right channel : aOutputR = aInputR * gainR */ + vscaled0 = _mm_mul_ps(vinr0, vgainr); + vscaled1 = _mm_mul_ps(vinr1, vgainr); + _mm_store_ps(&aOutputR[i], vscaled0); + _mm_store_ps(&aOutputR[i+4], vscaled1); + } + } else { + for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; i+=8) { + vinl0 = _mm_load_ps(&aInputL[i]); + vinr0 = _mm_load_ps(&aInputR[i]); + vinl1 = _mm_load_ps(&aInputL[i+4]); + vinr1 = _mm_load_ps(&aInputR[i+4]); + + /* left channel : aInputL * gainL */ + vscaled0 = _mm_mul_ps(vinl0, vgainl); + vscaled1 = _mm_mul_ps(vinl1, vgainl); + _mm_store_ps(&aOutputL[i], vscaled0); + _mm_store_ps(&aOutputL[i+4], vscaled1); + + /* right channel: aOutputR = aInputR + aInputL * gainR */ + vscaled0 = _mm_mul_ps(vinl0, vgainr); + vscaled1 = _mm_mul_ps(vinl1, vgainr); + vout0 = _mm_add_ps(vscaled0, vinr0); + vout1 = _mm_add_ps(vscaled1, vinr1); + _mm_store_ps(&aOutputR[i], vout0); + _mm_store_ps(&aOutputR[i+4], vout1); + } + } +} + +void BufferComplexMultiply_SSE(const float* aInput, + const float* aScale, + float* aOutput, + uint32_t aSize) +{ + unsigned i; + __m128 in0, in1, in2, in3, + outreal0, outreal1, outreal2, outreal3, + outimag0, outimag1, outimag2, outimag3; + + ASSERT_ALIGNED16(aInput); + ASSERT_ALIGNED16(aScale); + ASSERT_ALIGNED16(aOutput); + ASSERT_MULTIPLE16(aSize); + + for (i = 0; i < aSize * 2; i += 16) { + in0 = _mm_load_ps(&aInput[i]); + in1 = _mm_load_ps(&aInput[i + 4]); + in2 = _mm_load_ps(&aInput[i + 8]); + in3 = _mm_load_ps(&aInput[i + 12]); + + outreal0 = _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(2, 0, 2, 0)); + outimag0 = _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(3, 1, 3, 1)); + outreal2 = _mm_shuffle_ps(in2, in3, _MM_SHUFFLE(2, 0, 2, 0)); + outimag2 = _mm_shuffle_ps(in2, in3, _MM_SHUFFLE(3, 1, 3, 1)); + + in0 = _mm_load_ps(&aScale[i]); + in1 = _mm_load_ps(&aScale[i + 4]); + in2 = _mm_load_ps(&aScale[i + 8]); + in3 = _mm_load_ps(&aScale[i + 12]); + + outreal1 = _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(2, 0, 2, 0)); + outimag1 = _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(3, 1, 3, 1)); + outreal3 = _mm_shuffle_ps(in2, in3, _MM_SHUFFLE(2, 0, 2, 0)); + outimag3 = _mm_shuffle_ps(in2, in3, _MM_SHUFFLE(3, 1, 3, 1)); + + in0 = _mm_sub_ps(_mm_mul_ps(outreal0, outreal1), + _mm_mul_ps(outimag0, outimag1)); + in1 = _mm_add_ps(_mm_mul_ps(outreal0, outimag1), + _mm_mul_ps(outimag0, outreal1)); + in2 = _mm_sub_ps(_mm_mul_ps(outreal2, outreal3), + _mm_mul_ps(outimag2, outimag3)); + in3 = _mm_add_ps(_mm_mul_ps(outreal2, outimag3), + _mm_mul_ps(outimag2, outreal3)); + + outreal0 = _mm_unpacklo_ps(in0, in1); + outreal1 = _mm_unpackhi_ps(in0, in1); + outreal2 = _mm_unpacklo_ps(in2, in3); + outreal3 = _mm_unpackhi_ps(in2, in3); + + _mm_store_ps(&aOutput[i], outreal0); + _mm_store_ps(&aOutput[i + 4], outreal1); + _mm_store_ps(&aOutput[i + 8], outreal2); + _mm_store_ps(&aOutput[i + 12], outreal3); + } +} + +float +AudioBufferSumOfSquares_SSE(const float* aInput, uint32_t aLength) +{ + unsigned i; + __m128 in0, in1, in2, in3, + acc0, acc1, acc2, acc3; + float out[4]; + + ASSERT_ALIGNED16(aInput); + ASSERT_MULTIPLE16(aLength); + + acc0 = _mm_setzero_ps(); + acc1 = _mm_setzero_ps(); + acc2 = _mm_setzero_ps(); + acc3 = _mm_setzero_ps(); + + for (i = 0; i < aLength; i+=16) { + in0 = _mm_load_ps(&aInput[i]); + in1 = _mm_load_ps(&aInput[i + 4]); + in2 = _mm_load_ps(&aInput[i + 8]); + in3 = _mm_load_ps(&aInput[i + 12]); + + in0 = _mm_mul_ps(in0, in0); + in1 = _mm_mul_ps(in1, in1); + in2 = _mm_mul_ps(in2, in2); + in3 = _mm_mul_ps(in3, in3); + + acc0 = _mm_add_ps(acc0, in0); + acc1 = _mm_add_ps(acc1, in1); + acc2 = _mm_add_ps(acc2, in2); + acc3 = _mm_add_ps(acc3, in3); + } + + acc0 = _mm_add_ps(acc0, acc1); + acc0 = _mm_add_ps(acc0, acc2); + acc0 = _mm_add_ps(acc0, acc3); + + _mm_store_ps(out, acc0); + + return out[0] + out[1] + out[2] + out[3]; +} + +} diff --git a/dom/media/webaudio/AudioNodeEngineSSE2.h b/dom/media/webaudio/AudioNodeEngineSSE2.h new file mode 100644 index 0000000000..d24641249a --- /dev/null +++ b/dom/media/webaudio/AudioNodeEngineSSE2.h @@ -0,0 +1,45 @@ +/* -*- 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 "AudioNodeEngine.h" + +namespace mozilla { +void +AudioBufferAddWithScale_SSE(const float* aInput, + float aScale, + float* aOutput, + uint32_t aSize); + +void +AudioBlockCopyChannelWithScale_SSE(const float* aInput, + float aScale, + float* aOutput); + +void +AudioBlockCopyChannelWithScale_SSE(const float aInput[WEBAUDIO_BLOCK_SIZE], + const float aScale[WEBAUDIO_BLOCK_SIZE], + float aOutput[WEBAUDIO_BLOCK_SIZE]); + +void +AudioBufferInPlaceScale_SSE(float* aBlock, + float aScale, + uint32_t aSize); + +void +AudioBlockPanStereoToStereo_SSE(const float aInputL[WEBAUDIO_BLOCK_SIZE], + const float aInputR[WEBAUDIO_BLOCK_SIZE], + float aGainL, float aGainR, bool aIsOnTheLeft, + float aOutputL[WEBAUDIO_BLOCK_SIZE], + float aOutputR[WEBAUDIO_BLOCK_SIZE]); + +float +AudioBufferSumOfSquares_SSE(const float* aInput, uint32_t aLength); + +void +BufferComplexMultiply_SSE(const float* aInput, + const float* aScale, + float* aOutput, + uint32_t aSize); +} diff --git a/dom/media/webaudio/AudioNodeExternalInputStream.cpp b/dom/media/webaudio/AudioNodeExternalInputStream.cpp index 4c711fc0c0..e7bb353e46 100644 --- a/dom/media/webaudio/AudioNodeExternalInputStream.cpp +++ b/dom/media/webaudio/AudioNodeExternalInputStream.cpp @@ -3,6 +3,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "AlignedTArray.h" +#include "AlignmentUtils.h" #include "AudioNodeEngine.h" #include "AudioNodeExternalInputStream.h" #include "AudioChannelFormat.h" @@ -90,9 +92,20 @@ static void ConvertSegmentToAudioBlock(AudioSegment* aSegment, NS_ASSERTION(!ci.IsEnded(), "Should be at least one chunk!"); if (ci->GetDuration() == WEBAUDIO_BLOCK_SIZE && (ci->IsNull() || ci->mBufferFormat == AUDIO_FORMAT_FLOAT32)) { + + bool aligned = true; + for (size_t i = 0; i < ci->mChannelData.Length(); ++i) { + if (!IS_ALIGNED16(ci->mChannelData[i])) { + aligned = false; + break; + } + } + // Return this chunk directly to avoid copying data. - *aBlock = *ci; - return; + if (aligned) { + *aBlock = *ci; + return; + } } } @@ -109,8 +122,12 @@ static void ConvertSegmentToAudioBlock(AudioSegment* aSegment, CopyChunkToBlock(*ci, aBlock, duration); break; } - case AUDIO_FORMAT_SILENCE: + case AUDIO_FORMAT_SILENCE: { + // The actual type of the sample does not matter here, but we still need + // to send some audio to the graph. + CopyChunkToBlock(*ci, aBlock, duration); break; + } } duration += ci->GetDuration(); } @@ -188,7 +205,10 @@ AudioNodeExternalInputStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t accumulateIndex = 0; if (inputChannels) { - AutoTArray downmixBuffer; + // TODO: See Bug 1261168. Ideally we would use an aligned version of + // AutoTArray (of size GUESS_AUDIO_CHANNELS*WEBAUDIO_BLOCK_SIZE) here. + AlignedTArray downmixBuffer; + downmixBuffer.SetLength(GUESS_AUDIO_CHANNELS*WEBAUDIO_BLOCK_SIZE); for (uint32_t i = 0; i < audioSegments.Length(); ++i) { AudioBlock tmpChunk; ConvertSegmentToAudioBlock(&audioSegments[i], &tmpChunk, inputChannels); diff --git a/dom/media/webaudio/AudioNodeStream.cpp b/dom/media/webaudio/AudioNodeStream.cpp index d388d50811..25030e7745 100644 --- a/dom/media/webaudio/AudioNodeStream.cpp +++ b/dom/media/webaudio/AudioNodeStream.cpp @@ -454,8 +454,10 @@ AudioNodeStream::ObtainInputBlock(AudioBlock& aTmpChunk, } aTmpChunk.AllocateChannels(outputChannelCount); - // The static storage here should be 1KB, so it's fine - AutoTArray downmixBuffer; + // TODO: See Bug 1261168. Ideally we would use an aligned version of + // AutoTArray (of size GUESS_AUDIO_CHANNELS*WEBAUDIO_BLOCK_SIZE) here. + AlignedTArray downmixBuffer; + downmixBuffer.SetLength(GUESS_AUDIO_CHANNELS*WEBAUDIO_BLOCK_SIZE); for (uint32_t i = 0; i < inputChunkCount; ++i) { AccumulateInputChunk(i, *inputChunks[i], &aTmpChunk, &downmixBuffer); @@ -466,7 +468,7 @@ void AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex, const AudioBlock& aChunk, AudioBlock* aBlock, - nsTArray* aDownmixBuffer) + AlignedTArray* aDownmixBuffer) { AutoTArray channels; UpMixDownMixChunk(&aChunk, aBlock->ChannelCount(), channels, *aDownmixBuffer); @@ -492,7 +494,7 @@ void AudioNodeStream::UpMixDownMixChunk(const AudioBlock* aChunk, uint32_t aOutputChannelCount, nsTArray& aOutputChannels, - nsTArray& aDownmixBuffer) + AlignedTArray& aDownmixBuffer) { for (uint32_t i = 0; i < aChunk->ChannelCount(); i++) { aOutputChannels.AppendElement(static_cast(aChunk->mChannelData[i])); diff --git a/dom/media/webaudio/AudioNodeStream.h b/dom/media/webaudio/AudioNodeStream.h index 2882578c35..c0666923dc 100644 --- a/dom/media/webaudio/AudioNodeStream.h +++ b/dom/media/webaudio/AudioNodeStream.h @@ -8,6 +8,7 @@ #include "MediaStreamGraph.h" #include "mozilla/dom/AudioNodeBinding.h" +#include "AlignedTArray.h" #include "AudioBlock.h" namespace mozilla { @@ -190,10 +191,10 @@ protected: void FinishOutput(); void AccumulateInputChunk(uint32_t aInputIndex, const AudioBlock& aChunk, AudioBlock* aBlock, - nsTArray* aDownmixBuffer); + AlignedTArray* aDownmixBuffer); void UpMixDownMixChunk(const AudioBlock* aChunk, uint32_t aOutputChannelCount, nsTArray& aOutputChannels, - nsTArray& aDownmixBuffer); + AlignedTArray& aDownmixBuffer); uint32_t ComputedNumberOfChannels(uint32_t aInputChannelCount); void ObtainInputBlock(AudioBlock& aTmpChunk, uint32_t aPortIndex); diff --git a/dom/media/webaudio/BiquadFilterNode.cpp b/dom/media/webaudio/BiquadFilterNode.cpp index 2d0e411c05..25c86e8516 100644 --- a/dom/media/webaudio/BiquadFilterNode.cpp +++ b/dom/media/webaudio/BiquadFilterNode.cpp @@ -5,6 +5,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "BiquadFilterNode.h" +#include "AlignmentUtils.h" #include "AudioNodeEngine.h" #include "AudioNodeStream.h" #include "AudioDestinationNode.h" @@ -137,7 +138,9 @@ public: AudioBlock* aOutput, bool* aFinished) override { - float inputBuffer[WEBAUDIO_BLOCK_SIZE]; + float inputBuffer[WEBAUDIO_BLOCK_SIZE + 4]; + float* alignedInputBuffer = ALIGNED16(inputBuffer); + ASSERT_ALIGNED16(alignedInputBuffer); if (aInput.IsNull()) { bool hasTail = false; @@ -191,12 +194,12 @@ public: for (uint32_t i = 0; i < numberOfChannels; ++i) { const float* input; if (aInput.IsNull()) { - input = inputBuffer; + input = alignedInputBuffer; } else { input = static_cast(aInput.mChannelData[i]); if (aInput.mVolume != 1.0) { - AudioBlockCopyChannelWithScale(input, aInput.mVolume, inputBuffer); - input = inputBuffer; + AudioBlockCopyChannelWithScale(input, aInput.mVolume, alignedInputBuffer); + input = alignedInputBuffer; } } SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune); diff --git a/dom/media/webaudio/ConvolverNode.cpp b/dom/media/webaudio/ConvolverNode.cpp index d7ab7f26da..a35be7675d 100644 --- a/dom/media/webaudio/ConvolverNode.cpp +++ b/dom/media/webaudio/ConvolverNode.cpp @@ -6,6 +6,7 @@ #include "ConvolverNode.h" #include "mozilla/dom/ConvolverNodeBinding.h" +#include "AlignmentUtils.h" #include "AudioNodeEngine.h" #include "AudioNodeStream.h" #include "blink/Reverb.h" @@ -261,11 +262,13 @@ ConvolverNode::SetBuffer(JSContext* aCx, AudioBuffer* aBuffer, ErrorResult& aRv) length = WEBAUDIO_BLOCK_SIZE; RefPtr paddedBuffer = new ThreadSharedFloatArrayBufferList(data->GetChannels()); - float* channelData = (float*) moz_malloc(sizeof(float) * length * data->GetChannels()); + void* channelData = moz_malloc(sizeof(float) * length * data->GetChannels() + 15); + float* alignedChannelData = ALIGNED16(channelData); + ASSERT_ALIGNED16(alignedChannelData); for (uint32_t i = 0; i < data->GetChannels(); ++i) { - PodCopy(channelData + length * i, data->GetData(i), mBuffer->Length()); - PodZero(channelData + length * i + mBuffer->Length(), WEBAUDIO_BLOCK_SIZE - mBuffer->Length()); - paddedBuffer->SetData(i, (i == 0) ? channelData : nullptr, free, channelData); + PodCopy(alignedChannelData + length * i, data->GetData(i), mBuffer->Length()); + PodZero(alignedChannelData + length * i + mBuffer->Length(), WEBAUDIO_BLOCK_SIZE - mBuffer->Length()); + paddedBuffer->SetData(i, (i == 0) ? channelData : nullptr, free, alignedChannelData); } data = paddedBuffer; } diff --git a/dom/media/webaudio/GainNode.cpp b/dom/media/webaudio/GainNode.cpp index 4fb32e8797..a25aed1bad 100644 --- a/dom/media/webaudio/GainNode.cpp +++ b/dom/media/webaudio/GainNode.cpp @@ -6,6 +6,7 @@ #include "GainNode.h" #include "mozilla/dom/GainNodeBinding.h" +#include "AlignmentUtils.h" #include "AudioNodeEngine.h" #include "AudioNodeStream.h" #include "AudioDestinationNode.h" @@ -79,18 +80,20 @@ public: // Compute the gain values for the duration of the input AudioChunk StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom); - float computedGain[WEBAUDIO_BLOCK_SIZE]; - mGain.GetValuesAtTime(tick, computedGain, WEBAUDIO_BLOCK_SIZE); + float computedGain[WEBAUDIO_BLOCK_SIZE + 4]; + float* alignedComputedGain = ALIGNED16(computedGain); + ASSERT_ALIGNED16(alignedComputedGain); + mGain.GetValuesAtTime(tick, alignedComputedGain, WEBAUDIO_BLOCK_SIZE); for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) { - computedGain[counter] *= aInput.mVolume; + alignedComputedGain[counter] *= aInput.mVolume; } // Apply the gain to the output buffer for (size_t channel = 0; channel < aOutput->ChannelCount(); ++channel) { const float* inputBuffer = static_cast (aInput.mChannelData[channel]); float* buffer = aOutput->ChannelFloatsForWrite(channel); - AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer); + AudioBlockCopyChannelWithScale(inputBuffer, alignedComputedGain, buffer); } } } diff --git a/dom/media/webaudio/OscillatorNode.cpp b/dom/media/webaudio/OscillatorNode.cpp index 14344b87f2..586068dee5 100644 --- a/dom/media/webaudio/OscillatorNode.cpp +++ b/dom/media/webaudio/OscillatorNode.cpp @@ -38,8 +38,11 @@ public: , mDetune(0.f) , mType(OscillatorType::Sine) , mPhase(0.) + , mFinalFrequency(0.) + , mPhaseIncrement(0.) , mRecomputeParameters(true) , mCustomLength(0) + , mCustomDisableNormalization(false) { MOZ_ASSERT(NS_IsMainThread()); mBasicWaveFormCache = aDestination->Context()->GetBasicWaveFormCache(); @@ -54,7 +57,8 @@ public: FREQUENCY, DETUNE, TYPE, - PERIODICWAVE, + PERIODICWAVE_LENGTH, + DISABLE_NORMALIZATION, START, STOP, }; @@ -102,6 +106,7 @@ public: if (mType == OscillatorType::Sine) { // Forget any previous custom data. mCustomLength = 0; + mCustomDisableNormalization = false; mCustom = nullptr; mPeriodicWave = nullptr; mRecomputeParameters = true; @@ -122,10 +127,14 @@ public: } // End type switch. break; - case PERIODICWAVE: + case PERIODICWAVE_LENGTH: MOZ_ASSERT(aParam >= 0, "negative custom array length"); mCustomLength = static_cast(aParam); break; + case DISABLE_NORMALIZATION: + MOZ_ASSERT(aParam >= 0, "negative custom array length"); + mCustomDisableNormalization = static_cast(aParam); + break; default: NS_ERROR("Bad OscillatorNodeEngine Int32Parameter."); } @@ -139,7 +148,10 @@ public: MOZ_ASSERT(mCustom->GetChannels() == 2, "PeriodicWave should have sent two channels"); mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(), - mCustom->GetData(0), mCustom->GetData(1), mCustomLength); + mCustom->GetData(0), + mCustom->GetData(1), + mCustomLength, + mCustomDisableNormalization); } void IncrementPhase() @@ -153,14 +165,16 @@ public: } } - void UpdateParametersIfNeeded(StreamTime ticks, size_t count) + // Returns true if the final frequency (and thus the phase increment) changed, + // false otherwise. This allow some optimizations at callsite. + bool UpdateParametersIfNeeded(StreamTime ticks, size_t count) { double frequency, detune; // Shortcut if frequency-related AudioParam are not automated, and we // already have computed the frequency information and related parameters. if (!ParametersMayNeedUpdate()) { - return; + return false; } bool simpleFrequency = mFrequency.HasSimpleValue(); @@ -177,11 +191,17 @@ public: detune = mDetune.GetValueAtTime(ticks, count); } - mFinalFrequency = frequency * pow(2., detune / 1200.); - float signalPeriod = mSource->SampleRate() / mFinalFrequency; + float finalFrequency = frequency * pow(2., detune / 1200.); + float signalPeriod = mSource->SampleRate() / finalFrequency; mRecomputeParameters = false; mPhaseIncrement = 2 * M_PI / signalPeriod; + + if (finalFrequency != mFinalFrequency) { + mFinalFrequency = finalFrequency; + return true; + } + return false; } void FillBounds(float* output, StreamTime ticks, @@ -209,6 +229,8 @@ public: void ComputeSine(float * aOutput, StreamTime ticks, uint32_t aStart, uint32_t aEnd) { for (uint32_t i = aStart; i < aEnd; ++i) { + // We ignore the return value, changing the frequency has no impact on + // performances here. UpdateParametersIfNeeded(ticks, i); aOutput[i] = sin(mPhase); @@ -243,7 +265,7 @@ public: // mPhase runs [0,periodicWaveSize) here instead of [0,2*M_PI). float basePhaseIncrement = mPeriodicWave->rateScale(); - UpdateParametersIfNeeded(ticks, aStart); + bool needToFetchWaveData = UpdateParametersIfNeeded(ticks, aStart); bool parametersMayNeedUpdate = ParametersMayNeedUpdate(); mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency, @@ -253,11 +275,13 @@ public: for (uint32_t i = aStart; i < aEnd; ++i) { if (parametersMayNeedUpdate) { - mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency, - lowerWaveData, - higherWaveData, - tableInterpolationFactor); - UpdateParametersIfNeeded(ticks, i); + if (needToFetchWaveData) { + mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency, + lowerWaveData, + higherWaveData, + tableInterpolationFactor); + } + needToFetchWaveData = UpdateParametersIfNeeded(ticks, i); } // Bilinear interpolation between adjacent samples in each table. float floorPhase = floorf(mPhase); @@ -379,6 +403,7 @@ public: RefPtr mCustom; RefPtr mBasicWaveFormCache; uint32_t mCustomLength; + bool mCustomDisableNormalization; RefPtr mPeriodicWave; }; @@ -458,8 +483,10 @@ void OscillatorNode::SendPeriodicWaveToStream() "Sending custom waveform to engine thread with non-custom type"); MOZ_ASSERT(mStream, "Missing node stream."); MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object."); - SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE, + SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE_LENGTH, mPeriodicWave->DataLength()); + SendInt32ParameterToStream(OscillatorNodeEngine::DISABLE_NORMALIZATION, + mPeriodicWave->DisableNormalization()); RefPtr data = mPeriodicWave->GetThreadSharedBuffer(); mStream->SetBuffer(data.forget()); diff --git a/dom/media/webaudio/PeriodicWave.cpp b/dom/media/webaudio/PeriodicWave.cpp index 12beaa691a..c8a7473fb0 100644 --- a/dom/media/webaudio/PeriodicWave.cpp +++ b/dom/media/webaudio/PeriodicWave.cpp @@ -20,8 +20,10 @@ PeriodicWave::PeriodicWave(AudioContext* aContext, const float* aRealData, const float* aImagData, const uint32_t aLength, + const bool aDisableNormalization, ErrorResult& aRv) : mContext(aContext) + , mDisableNormalization(aDisableNormalization) { MOZ_ASSERT(aContext); diff --git a/dom/media/webaudio/PeriodicWave.h b/dom/media/webaudio/PeriodicWave.h index d4962f6648..b67d597e4d 100644 --- a/dom/media/webaudio/PeriodicWave.h +++ b/dom/media/webaudio/PeriodicWave.h @@ -24,6 +24,7 @@ public: const float* aRealData, const float* aImagData, const uint32_t aLength, + const bool aDisableNormalization, ErrorResult& aRv); NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PeriodicWave) @@ -41,6 +42,11 @@ public: return mLength; } + bool DisableNormalization() const + { + return mDisableNormalization; + } + ThreadSharedFloatArrayBufferList* GetThreadSharedBuffer() const { return mCoefficients; @@ -55,6 +61,7 @@ private: RefPtr mContext; RefPtr mCoefficients; uint32_t mLength; + bool mDisableNormalization; }; } // namespace dom diff --git a/dom/media/webaudio/StereoPannerNode.cpp b/dom/media/webaudio/StereoPannerNode.cpp index be885a1d16..9441f84685 100644 --- a/dom/media/webaudio/StereoPannerNode.cpp +++ b/dom/media/webaudio/StereoPannerNode.cpp @@ -9,6 +9,7 @@ #include "AudioNodeEngine.h" #include "AudioNodeStream.h" #include "AudioDestinationNode.h" +#include "AlignmentUtils.h" #include "WebAudioUtils.h" #include "PanningUtils.h" #include "AudioParamTimeline.h" @@ -136,24 +137,26 @@ public: panning <= 0); } } else { - float computedGain[2][WEBAUDIO_BLOCK_SIZE]; + float computedGain[2*WEBAUDIO_BLOCK_SIZE + 4]; bool onLeft[WEBAUDIO_BLOCK_SIZE]; float values[WEBAUDIO_BLOCK_SIZE]; StreamTime tick = mDestination->GraphTimeToStreamTime(aFrom); mPan.GetValuesAtTime(tick, values, WEBAUDIO_BLOCK_SIZE); + float* alignedComputedGain = ALIGNED16(computedGain); + ASSERT_ALIGNED16(alignedComputedGain); for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) { float left, right; GetGainValuesForPanning(values[counter], monoToStereo, left, right); - computedGain[0][counter] = left * aInput.mVolume; - computedGain[1][counter] = right * aInput.mVolume; + alignedComputedGain[counter] = left * aInput.mVolume; + alignedComputedGain[WEBAUDIO_BLOCK_SIZE + counter] = right * aInput.mVolume; onLeft[counter] = values[counter] <= 0; } // Apply the gain to the output buffer - ApplyStereoPanning(aInput, aOutput, computedGain[0], computedGain[1], onLeft); + ApplyStereoPanning(aInput, aOutput, alignedComputedGain, &alignedComputedGain[WEBAUDIO_BLOCK_SIZE], onLeft); } } diff --git a/dom/media/webaudio/WaveShaperNode.cpp b/dom/media/webaudio/WaveShaperNode.cpp index 2d2bdba097..ee3e9cb10e 100644 --- a/dom/media/webaudio/WaveShaperNode.cpp +++ b/dom/media/webaudio/WaveShaperNode.cpp @@ -6,6 +6,7 @@ #include "WaveShaperNode.h" #include "mozilla/dom/WaveShaperNodeBinding.h" +#include "AlignmentUtils.h" #include "AudioNode.h" #include "AudioNodeEngine.h" #include "AudioNodeStream.h" @@ -231,13 +232,15 @@ public: aOutput->AllocateChannels(channelCount); for (uint32_t i = 0; i < channelCount; ++i) { const float* inputSamples; - float scaledInput[WEBAUDIO_BLOCK_SIZE]; + float scaledInput[WEBAUDIO_BLOCK_SIZE + 4]; + float* alignedScaledInput = ALIGNED16(scaledInput); + ASSERT_ALIGNED16(alignedScaledInput); if (aInput.mVolume != 1.0f) { AudioBlockCopyChannelWithScale( static_cast(aInput.mChannelData[i]), aInput.mVolume, - scaledInput); - inputSamples = scaledInput; + alignedScaledInput); + inputSamples = alignedScaledInput; } else { inputSamples = static_cast(aInput.mChannelData[i]); } diff --git a/dom/media/webaudio/WebAudioUtils.cpp b/dom/media/webaudio/WebAudioUtils.cpp index ee869ccc82..db9fbc3846 100644 --- a/dom/media/webaudio/WebAudioUtils.cpp +++ b/dom/media/webaudio/WebAudioUtils.cpp @@ -10,6 +10,8 @@ namespace mozilla { +LazyLogModule gWebAudioAPILog("WebAudioAPI"); + namespace dom { void WebAudioUtils::ConvertAudioTimelineEventToTicks(AudioTimelineEvent& aEvent, diff --git a/dom/media/webaudio/WebAudioUtils.h b/dom/media/webaudio/WebAudioUtils.h index b15795c20e..dcf0037122 100644 --- a/dom/media/webaudio/WebAudioUtils.h +++ b/dom/media/webaudio/WebAudioUtils.h @@ -20,6 +20,10 @@ namespace mozilla { class AudioNodeStream; +extern LazyLogModule gWebAudioAPILog; +#define WEB_AUDIO_API_LOG(...) \ + MOZ_LOG(gWebAudioAPILog, LogLevel::Debug, (__VA_ARGS__)) + namespace dom { struct AudioTimelineEvent; diff --git a/dom/media/webaudio/blink/PeriodicWave.cpp b/dom/media/webaudio/blink/PeriodicWave.cpp index 763c52fecb..3f949207a3 100644 --- a/dom/media/webaudio/blink/PeriodicWave.cpp +++ b/dom/media/webaudio/blink/PeriodicWave.cpp @@ -45,13 +45,15 @@ already_AddRefed PeriodicWave::create(float sampleRate, const float* real, const float* imag, - size_t numberOfComponents) + size_t numberOfComponents, + bool disableNormalization) { bool isGood = real && imag && numberOfComponents > 0; MOZ_ASSERT(isGood); if (isGood) { RefPtr periodicWave = - new PeriodicWave(sampleRate, numberOfComponents); + new PeriodicWave(sampleRate, numberOfComponents, + disableNormalization); // Limit the number of components used to those for frequencies below the // Nyquist of the fixed length inverse FFT. @@ -74,7 +76,7 @@ already_AddRefed PeriodicWave::createSine(float sampleRate) { RefPtr periodicWave = - new PeriodicWave(sampleRate, MinPeriodicWaveSize); + new PeriodicWave(sampleRate, MinPeriodicWaveSize, false); periodicWave->generateBasicWaveform(OscillatorType::Sine); return periodicWave.forget(); } @@ -83,7 +85,7 @@ already_AddRefed PeriodicWave::createSquare(float sampleRate) { RefPtr periodicWave = - new PeriodicWave(sampleRate, MinPeriodicWaveSize); + new PeriodicWave(sampleRate, MinPeriodicWaveSize, false); periodicWave->generateBasicWaveform(OscillatorType::Square); return periodicWave.forget(); } @@ -92,7 +94,7 @@ already_AddRefed PeriodicWave::createSawtooth(float sampleRate) { RefPtr periodicWave = - new PeriodicWave(sampleRate, MinPeriodicWaveSize); + new PeriodicWave(sampleRate, MinPeriodicWaveSize, false); periodicWave->generateBasicWaveform(OscillatorType::Sawtooth); return periodicWave.forget(); } @@ -101,15 +103,17 @@ already_AddRefed PeriodicWave::createTriangle(float sampleRate) { RefPtr periodicWave = - new PeriodicWave(sampleRate, MinPeriodicWaveSize); + new PeriodicWave(sampleRate, MinPeriodicWaveSize, false); periodicWave->generateBasicWaveform(OscillatorType::Triangle); return periodicWave.forget(); } -PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents) +PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents, bool disableNormalization) : m_sampleRate(sampleRate) , m_centsPerRange(CentsPerRange) - , m_lowestRequestedFundamentalFrequency(std::numeric_limits::max()) + , m_maxPartialsInBandLimitedTable(0) + , m_normalizationScale(1.0f) + , m_disableNormalization(disableNormalization) { float nyquist = 0.5 * m_sampleRate; @@ -147,7 +151,15 @@ void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, f // to the positive frequency. fundamentalFrequency = fabsf(fundamentalFrequency); - if (fundamentalFrequency < m_lowestRequestedFundamentalFrequency) { + // We only need to rebuild to the tables if the new fundamental + // frequency is low enough to allow for more partials below the + // Nyquist frequency. + unsigned numberOfPartials = numberOfPartialsForRange(0); + float nyquist = 0.5 * m_sampleRate; + if (fundamentalFrequency != 0.0) { + numberOfPartials = std::min(numberOfPartials, (unsigned)(nyquist / fundamentalFrequency)); + } + if (numberOfPartials > m_maxPartialsInBandLimitedTable) { for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) { m_bandLimitedTables[rangeIndex] = 0; } @@ -155,7 +167,7 @@ void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, f // We need to create the first table to determine the normalization // constant. createBandLimitedTables(fundamentalFrequency, 0); - m_lowestRequestedFundamentalFrequency = fundamentalFrequency; + m_maxPartialsInBandLimitedTable = numberOfPartials; } // Calculate the pitch range. @@ -233,8 +245,10 @@ void PeriodicWave::createBandLimitedTables(float fundamentalFrequency, // Limit number of partials to those below Nyquist frequency float nyquist = 0.5 * m_sampleRate; - numberOfPartials = std::min(numberOfPartials, - (unsigned)(nyquist / fundamentalFrequency)); + if (fundamentalFrequency != 0.0) { + numberOfPartials = std::min(numberOfPartials, + (unsigned)(nyquist / fundamentalFrequency)); + } // Copy from loaded frequency data and generate complex conjugate // because of the way the inverse FFT is defined. @@ -260,7 +274,7 @@ void PeriodicWave::createBandLimitedTables(float fundamentalFrequency, // For the first range (which has the highest power), calculate // its peak value then compute normalization scale. - if (!rangeIndex) { + if (!m_disableNormalization && !rangeIndex) { float maxValue; maxValue = AudioBufferPeakValue(data, m_periodicWaveSize); @@ -269,7 +283,9 @@ void PeriodicWave::createBandLimitedTables(float fundamentalFrequency, } // Apply normalization scale. - AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize); + if (!m_disableNormalization) { + AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize); + } } void PeriodicWave::generateBasicWaveform(OscillatorType shape) diff --git a/dom/media/webaudio/blink/PeriodicWave.h b/dom/media/webaudio/blink/PeriodicWave.h index a0b6cdd111..47381d450a 100644 --- a/dom/media/webaudio/blink/PeriodicWave.h +++ b/dom/media/webaudio/blink/PeriodicWave.h @@ -54,7 +54,8 @@ public: static already_AddRefed create(float sampleRate, const float* real, const float* imag, - size_t numberOfComponents); + size_t numberOfComponents, + bool disableNormalization); // Returns pointers to the lower and higher wave data for the pitch range // containing the given fundamental frequency. These two tables are in @@ -76,7 +77,7 @@ public: size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; private: - explicit PeriodicWave(float sampleRate, size_t numberOfComponents); + explicit PeriodicWave(float sampleRate, size_t numberOfComponents, bool disableNormalization); ~PeriodicWave() {} void generateBasicWaveform(mozilla::dom::OscillatorType); @@ -106,8 +107,9 @@ private: // Creates table for specified index based on fundamental frequency. void createBandLimitedTables(float fundamentalFrequency, unsigned rangeIndex); - float m_lowestRequestedFundamentalFrequency; + unsigned m_maxPartialsInBandLimitedTable; float m_normalizationScale; + bool m_disableNormalization; nsTArray > m_bandLimitedTables; }; diff --git a/dom/media/webaudio/blink/ReverbAccumulationBuffer.cpp b/dom/media/webaudio/blink/ReverbAccumulationBuffer.cpp index 18d2228944..2dfd99693d 100644 --- a/dom/media/webaudio/blink/ReverbAccumulationBuffer.cpp +++ b/dom/media/webaudio/blink/ReverbAccumulationBuffer.cpp @@ -96,12 +96,30 @@ int ReverbAccumulationBuffer::accumulate(const float* source, size_t numberOfFra if (!isSafe) return 0; - AudioBufferAddWithScale(source, 1.0f, destination + writeIndex, numberOfFrames1); +#ifdef USE_SSE2 + // It is unlikely either the source is aligned or the number of values + // is a multiple of 16, so we just add them here rather than calling + // AudioBufferAddWithScale. + // + // TODO: Ideally we would use scalar calls when necessary and switch + // to vector calls when we have aligned sources and destinations. + // See Bug 1263910. + for (uint32_t i = 0; i < numberOfFrames1; ++i) { + destination[writeIndex + i] += source[i]; + } - // Handle wrap-around if necessary + // Handle wrap-around if necessary. + if (numberOfFrames2 > 0) { + for (uint32_t i = 0; i < numberOfFrames2; ++i) { + destination[i] += source[numberOfFrames1 + i]; + } + } +#else + AudioBufferAddWithScale(source, 1.0f, destination + writeIndex, numberOfFrames1); if (numberOfFrames2 > 0) { AudioBufferAddWithScale(source + numberOfFrames1, 1.0f, destination, numberOfFrames2); } +#endif return writeIndex; } diff --git a/dom/media/webaudio/blink/ReverbAccumulationBuffer.h b/dom/media/webaudio/blink/ReverbAccumulationBuffer.h index 97ee94d271..a37741a2e5 100644 --- a/dom/media/webaudio/blink/ReverbAccumulationBuffer.h +++ b/dom/media/webaudio/blink/ReverbAccumulationBuffer.h @@ -29,13 +29,11 @@ #ifndef ReverbAccumulationBuffer_h #define ReverbAccumulationBuffer_h -#include "nsTArray.h" +#include "AlignedTArray.h" #include "mozilla/MemoryReporting.h" namespace WebCore { -typedef nsTArray AudioFloatArray; - // ReverbAccumulationBuffer is a circular delay buffer with one client reading from it and multiple clients // writing/accumulating to it at different delay offsets from the read position. The read operation will zero the memory // just read from the buffer, so it will be ready for accumulation the next time around. @@ -65,7 +63,7 @@ public: } private: - AudioFloatArray m_buffer; + AlignedTArray m_buffer; size_t m_readIndex; size_t m_readTimeFrame; // for debugging (frame on continuous timeline) }; diff --git a/dom/media/webaudio/blink/moz.build b/dom/media/webaudio/blink/moz.build index fd622d0749..5af26ad282 100644 --- a/dom/media/webaudio/blink/moz.build +++ b/dom/media/webaudio/blink/moz.build @@ -23,6 +23,10 @@ UNIFIED_SOURCES += [ 'ZeroPole.cpp', ] +# Are we targeting x86 or x64? If so, build SSE2 files. +if CONFIG['INTEL_ARCHITECTURE']: + DEFINES['USE_SSE2'] = True + include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' diff --git a/dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp b/dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp index 76616c2f3a..44c0ab08a9 100644 --- a/dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp +++ b/dom/media/webaudio/compiledtest/TestAudioEventTimeline.cpp @@ -145,7 +145,8 @@ void TestSpecExample() is(timeline.GetValueAtTime(0.6), 0.75f, "Correct value"); is(timeline.GetValueAtTime(0.65), (0.75f * powf(0.05f / 0.75f, 0.5f)), "Correct value"); is(timeline.GetValueAtTime(0.7), -1.0f, "Correct value"); - is(timeline.GetValueAtTime(0.9), 0.0f, "Correct value"); + is(timeline.GetValueAtTime(0.8), 0.0f, "Correct value"); + is(timeline.GetValueAtTime(0.9), 1.0f, "Correct value"); is(timeline.GetValueAtTime(1.0), 1.0f, "Correct value"); } @@ -431,6 +432,19 @@ void TestExponentialInvalidPreviousZeroValue() is(rv, NS_OK, "Should succeed this time"); } +void +TestSettingValueCurveTwice() +{ + Timeline timeline(0.f); + float curve[] = { -1.0f, 0.0f, 1.0f }; + + ErrorResultMock rv; + + timeline.SetValueCurveAtTime(curve, ArrayLength(curve), 0.0f, 0.3f, rv); + timeline.SetValueCurveAtTime(curve, ArrayLength(curve), 0.0f, 0.3f, rv); + is(rv, NS_OK, "SetValueCurveAtTime succeeded"); +} + int main() { ScopedXPCOM xpcom("TestAudioEventTimeline"); @@ -456,6 +470,7 @@ int main() TestExponentialRampAtSameTime(); TestSetTargetZeroTimeConstant(); TestExponentialInvalidPreviousZeroValue(); + TestSettingValueCurveTwice(); return gFailCount > 0; } diff --git a/dom/media/webaudio/moz.build b/dom/media/webaudio/moz.build index fed52629e1..1c66d72fcb 100644 --- a/dom/media/webaudio/moz.build +++ b/dom/media/webaudio/moz.build @@ -108,6 +108,13 @@ if CONFIG['CPU_ARCH'] == 'arm' and CONFIG['BUILD_ARM_NEON']: '/media/openmax_dl/dl/api/' ] +# Are we targeting x86 or x64? If so, build SSE2 files. +if CONFIG['INTEL_ARCHITECTURE']: + SOURCES += ['AudioNodeEngineSSE2.cpp'] + DEFINES['USE_SSE2'] = True + SOURCES['AudioNodeEngineSSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] + + include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' diff --git a/dom/media/webaudio/test/mochitest.ini b/dom/media/webaudio/test/mochitest.ini index 6c0473e985..a23172d420 100644 --- a/dom/media/webaudio/test/mochitest.ini +++ b/dom/media/webaudio/test/mochitest.ini @@ -58,6 +58,7 @@ tags=capturestream [test_audioParamGain.html] [test_audioParamLinearRamp.html] [test_audioParamSetCurveAtTime.html] +[test_audioParamSetCurveAtTimeTwice.html] [test_audioParamSetCurveAtTimeZeroDuration.html] [test_audioParamSetTargetAtTime.html] [test_audioParamSetValueAtTime.html] diff --git a/dom/media/webaudio/test/test_OfflineAudioContext.html b/dom/media/webaudio/test/test_OfflineAudioContext.html index 8bd7d9d60f..81d3e2313c 100644 --- a/dom/media/webaudio/test/test_OfflineAudioContext.html +++ b/dom/media/webaudio/test/test_OfflineAudioContext.html @@ -33,6 +33,7 @@ SimpleTest.waitForExplicitFinish(); addLoadEvent(function() { var ctx = new OfflineAudioContext(2, 100, 22050); ok(ctx instanceof EventTarget, "OfflineAudioContexts must be EventTargets"); + is(ctx.length, 100, "OfflineAudioContext.length is equal to the value passed to the ctor."); var buf = ctx.createBuffer(2, 100, ctx.sampleRate); for (var i = 0; i < 2; ++i) { diff --git a/dom/media/webaudio/test/test_audioParamSetCurveAtTimeTwice.html b/dom/media/webaudio/test/test_audioParamSetCurveAtTimeTwice.html new file mode 100644 index 0000000000..3faac4edf3 --- /dev/null +++ b/dom/media/webaudio/test/test_audioParamSetCurveAtTimeTwice.html @@ -0,0 +1,62 @@ + + + + Test AudioParam.linearRampToValue + + + + + +
+
+
+ + diff --git a/dom/messages/SystemMessageInternal.js b/dom/messages/SystemMessageInternal.js index 3d7e70bbe0..684be71f14 100644 --- a/dom/messages/SystemMessageInternal.js +++ b/dom/messages/SystemMessageInternal.js @@ -30,7 +30,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "appsService", "nsIAppsService"); // Limit the number of pending messages for a given page. -let kMaxPendingMessages; +var kMaxPendingMessages; try { kMaxPendingMessages = Services.prefs.getIntPref("dom.messages.maxPendingMessages"); @@ -40,6 +40,7 @@ try { } const kMessages =["SystemMessageManager:GetPendingMessages", + "SystemMessageManager:HasPendingMessages", "SystemMessageManager:Register", "SystemMessageManager:Unregister", "SystemMessageManager:Message:Return:OK", @@ -53,7 +54,7 @@ function debug(aMsg) { } -let defaultMessageConfigurator = { +var defaultMessageConfigurator = { get mustShowRunningApp() { return false; } @@ -432,6 +433,7 @@ SystemMessageInternal.prototype = { // TODO: fix bug 988142 to re-enable. // "SystemMessageManager:Unregister", "SystemMessageManager:GetPendingMessages", + "SystemMessageManager:HasPendingMessages", "SystemMessageManager:Message:Return:OK", "SystemMessageManager:HandleMessagesDone", "SystemMessageManager:HandleMessageDone"].indexOf(aMessage.name) != -1) { @@ -532,6 +534,25 @@ SystemMessageInternal.prototype = { this._refreshCacheInternal(aMessage.target, msg.manifestURL); break; } + case "SystemMessageManager:HasPendingMessages": + { + debug("received SystemMessageManager:HasPendingMessages " + msg.type + + " for " + msg.pageURL + " @ " + msg.manifestURL); + + // NB: Sync message SystemMessageManager:HasPendingMessages + // should only be used by in-process app. For out-of-process + // app, SystemMessageCache should be used. + + // This is a sync call used to return if a page has pending messages. + // Find the right page to get its corresponding pending messages. + let page = this._findPage(msg.type, msg.pageURL, msg.manifestURL); + if (!page) { + return false; + } + + return page.pendingMessages.length != 0; + break; + } case "SystemMessageManager:Message:Return:OK": { debug("received SystemMessageManager:Message:Return:OK " + msg.type + diff --git a/dom/messages/SystemMessageManager.js b/dom/messages/SystemMessageManager.js index 799a361da2..41d58e87d8 100644 --- a/dom/messages/SystemMessageManager.js +++ b/dom/messages/SystemMessageManager.js @@ -194,6 +194,14 @@ SystemMessageManager.prototype = { } + // Use SystemMessageManager directly when we are in the same process. + if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT) { + return cpmm.sendSyncMessage("SystemMessageManager:HasPendingMessages", + { type: aType, + pageURL: this._pageURL, + manifestURL: this._manifestURL })[0]; + } + /* * NB: If the system message is fired after we received the cache * and before we registered the pageURL we will get false diff --git a/dom/messages/test/mochitest.ini b/dom/messages/test/mochitest.ini index 77d99cbf29..d99180da29 100644 --- a/dom/messages/test/mochitest.ini +++ b/dom/messages/test/mochitest.ini @@ -3,4 +3,4 @@ support-files = system_message_chrome_script.js [test_bug_993732.html] -skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage +skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage diff --git a/dom/messages/test/system_message_chrome_script.js b/dom/messages/test/system_message_chrome_script.js index 4d6358f517..b00d744ac1 100644 --- a/dom/messages/test/system_message_chrome_script.js +++ b/dom/messages/test/system_message_chrome_script.js @@ -3,7 +3,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ 'use strict'; -const { classes: Cc, interfaces: Ci } = Components; +var { classes: Cc, interfaces: Ci } = Components; const systemMessenger = Cc["@mozilla.org/system-message-internal;1"] .getService(Ci.nsISystemMessagesInternal); diff --git a/dom/mobileconnection/tests/marionette/head.js b/dom/mobileconnection/tests/marionette/head.js index afc92bae1b..6a7ac44bb8 100644 --- a/dom/mobileconnection/tests/marionette/head.js +++ b/dom/mobileconnection/tests/marionette/head.js @@ -24,8 +24,8 @@ function Deferred() { Object.freeze(this); } -let _pendingEmulatorCmdCount = 0; -let _pendingEmulatorShellCmdCount = 0; +var _pendingEmulatorCmdCount = 0; +var _pendingEmulatorShellCmdCount = 0; /** * Send emulator command with safe guard. @@ -93,7 +93,7 @@ function runEmulatorShellCmdSafe(aCommands) { return deferred.promise; } -let workingFrame; +var workingFrame; /** * Get mozSettings value specified by @aKey. @@ -222,7 +222,7 @@ function setDataApnSettings(aApnSettings, aAllowError) { return setSettings1(SETTINGS_KEY_DATA_APN_SETTINGS, aApnSettings, aAllowError); } -let mobileConnection; +var mobileConnection; /** * Push required permissions and test if @@ -1136,7 +1136,7 @@ function setEmulatorLteSignalStrengthAndWait(aRxlev, aRsrp, aRssnr, return Promise.all(promises); } -let _networkManager; +var _networkManager; /** * Get internal NetworkManager service. @@ -1151,7 +1151,7 @@ function getNetworkManager() { return _networkManager; } -let _numOfRadioInterfaces; +var _numOfRadioInterfaces; /* * Get number of radio interfaces. Default is 1 if preference is not set. diff --git a/dom/mobilemessage/android/MobileMessageDatabaseService.cpp b/dom/mobilemessage/android/MobileMessageDatabaseService.cpp deleted file mode 100644 index 397e2a2654..0000000000 --- a/dom/mobilemessage/android/MobileMessageDatabaseService.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "MobileMessageDatabaseService.h" - -#include "AndroidBridge.h" -#include "SmsManager.h" - -namespace mozilla { -namespace dom { -namespace mobilemessage { - -NS_IMPL_ISUPPORTS(MobileMessageDatabaseService, nsIMobileMessageDatabaseService) - -MobileMessageDatabaseService::MobileMessageDatabaseService() -{ - SmsManager::Init(); -} - -NS_IMETHODIMP -MobileMessageDatabaseService::GetMessageMoz(int32_t aMessageId, - nsIMobileMessageCallback* aRequest) -{ - if (!AndroidBridge::Bridge()) { - return NS_OK; - } - - AndroidBridge::Bridge()->GetMessage(aMessageId, aRequest); - return NS_OK; -} - -NS_IMETHODIMP -MobileMessageDatabaseService::DeleteMessage(int32_t *aMessageIds, - uint32_t aLength, - nsIMobileMessageCallback* aRequest) -{ - if (!AndroidBridge::Bridge()) { - return NS_OK; - } - - if (!aMessageIds) { - return NS_OK; - } - - if (aLength != 1) { - return NS_ERROR_FAILURE; - } - - AndroidBridge::Bridge()->DeleteMessage(aMessageIds[0], aRequest); - return NS_OK; -} - -NS_IMETHODIMP -MobileMessageDatabaseService::CreateMessageCursor(bool aHasStartDate, - uint64_t aStartDate, - bool aHasEndDate, - uint64_t aEndDate, - const char16_t** aNumbers, - uint32_t aNumbersCount, - const nsAString& aDelivery, - bool aHasRead, - bool aRead, - bool aHasThreadId, - uint64_t aThreadId, - bool aReverse, - nsIMobileMessageCursorCallback* aCallback, - nsICursorContinueCallback** aCursor) -{ - if (!AndroidBridge::Bridge()) { - *aCursor = nullptr; - return NS_OK; - } - - nsCOMPtr cursor = - AndroidBridge::Bridge()->CreateMessageCursor(aHasStartDate, - aStartDate, - aHasEndDate, - aEndDate, - aNumbers, - aNumbersCount, - aDelivery, - aHasRead, - aRead, - aHasThreadId, - aThreadId, - aReverse, - aCallback); - cursor.forget(aCursor); - return NS_OK; -} - -NS_IMETHODIMP -MobileMessageDatabaseService::MarkMessageRead(int32_t aMessageId, - bool aValue, - bool aSendReadReport, - nsIMobileMessageCallback* aRequest) -{ - if (!AndroidBridge::Bridge()) { - return NS_OK; - } - - AndroidBridge::Bridge()->MarkMessageRead(aMessageId, - aValue, - aSendReadReport, - aRequest); - return NS_OK; -} - -NS_IMETHODIMP -MobileMessageDatabaseService::CreateThreadCursor(nsIMobileMessageCursorCallback* aRequest, - nsICursorContinueCallback** aCursor) -{ - if (!AndroidBridge::Bridge()) { - *aCursor = nullptr; - return NS_OK; - } - - nsCOMPtr cursor = - AndroidBridge::Bridge()->CreateThreadCursor(aRequest); - cursor.forget(aCursor); - return NS_OK; -} - -} // namespace mobilemessage -} // namespace dom -} // namespace mozilla diff --git a/dom/mobilemessage/android/MobileMessageDatabaseService.h b/dom/mobilemessage/android/MobileMessageDatabaseService.h deleted file mode 100644 index 26758a1d7b..0000000000 --- a/dom/mobilemessage/android/MobileMessageDatabaseService.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. 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_mobilemessage_MobileMessageDatabaseService_h -#define mozilla_dom_mobilemessage_MobileMessageDatabaseService_h - -#include "nsIMobileMessageDatabaseService.h" -#include "mozilla/Attributes.h" - -namespace mozilla { -namespace dom { -namespace mobilemessage { - -class MobileMessageDatabaseService final : public nsIMobileMessageDatabaseService -{ -private: - ~MobileMessageDatabaseService() {} - -public: - MobileMessageDatabaseService(); - - NS_DECL_ISUPPORTS - NS_DECL_NSIMOBILEMESSAGEDATABASESERVICE -}; - -} // namespace mobilemessage -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_mobilemessage_MobileMessageDatabaseService_h diff --git a/dom/mobilemessage/android/SmsManager.cpp b/dom/mobilemessage/android/SmsManager.cpp deleted file mode 100644 index 3314379e32..0000000000 --- a/dom/mobilemessage/android/SmsManager.cpp +++ /dev/null @@ -1,429 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "SmsManager.h" - -#include "mozilla/dom/mobilemessage/Constants.h" -#include "mozilla/dom/mobilemessage/PSms.h" -#include "mozilla/dom/mobilemessage/SmsParent.h" -#include "mozilla/dom/mobilemessage/SmsTypes.h" -#include "mozilla/dom/mobilemessage/Types.h" -#include "MobileMessageThreadInternal.h" -#include "SmsMessageInternal.h" -#include "mozilla/Services.h" -#include "nsIMobileMessageDatabaseService.h" -#include "nsIObserverService.h" -#include "nsThreadUtils.h" - -using namespace mozilla::dom; -using namespace mozilla::dom::mobilemessage; - -namespace mozilla { - -/*static*/ -void -SmsManager::NotifySmsReceived(jni::String::Param aSender, - jni::String::Param aBody, - int32_t aMessageClass, - int64_t aTimestamp) -{ - // TODO Need to correct the message `threadId` parameter value. Bug 859098 - SmsMessageData message; - message.id() = 0; - message.threadId() = 0; - message.iccId() = EmptyString(); - message.delivery() = eDeliveryState_Received; - message.deliveryStatus() = eDeliveryStatus_Success; - message.sender() = aSender ? nsString(aSender) : EmptyString(); - message.receiver() = EmptyString(); - message.body() = aBody ? nsString(aBody) : EmptyString(); - message.messageClass() = static_cast(aMessageClass); - message.timestamp() = aTimestamp; - message.sentTimestamp() = aTimestamp; - message.deliveryTimestamp() = aTimestamp; - message.read() = false; - - nsCOMPtr runnable = NS_NewRunnableFunction([=] () { - nsCOMPtr obs = services::GetObserverService(); - if (!obs) { - return; - } - - nsCOMPtr domMessage = new SmsMessageInternal(message); - obs->NotifyObservers(domMessage, kSmsReceivedObserverTopic, nullptr); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifySmsSent(int32_t aId, - jni::String::Param aReceiver, - jni::String::Param aBody, - int64_t aTimestamp, - int32_t aRequestId) -{ - // TODO Need to add the message `messageClass` parameter value. Bug 804476 - // TODO Need to correct the message `threadId` parameter value. Bug 859098 - SmsMessageData message; - message.id() = aId; - message.threadId() = 0; - message.iccId() = EmptyString(); - message.delivery() = eDeliveryState_Sent; - message.deliveryStatus() = eDeliveryStatus_Pending; - message.sender() = EmptyString(); - message.receiver() = aReceiver ? nsString(aReceiver) : EmptyString(); - message.body() = aBody ? nsString(aBody) : EmptyString(); - message.messageClass() = eMessageClass_Normal; - message.timestamp() = aTimestamp; - message.sentTimestamp() = aTimestamp; - message.deliveryTimestamp() = aTimestamp; - message.read() = true; - - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - /* - * First, we are going to notify all SmsManager that a message has - * been sent. Then, we will notify the SmsRequest object about it. - */ - nsCOMPtr obs = services::GetObserverService(); - if (!obs) { - return; - } - - nsCOMPtr domMessage = new SmsMessageInternal(message); - obs->NotifyObservers(domMessage, kSmsSentObserverTopic, nullptr); - - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId); - if (!request) { - return; - } - - request->NotifyMessageSent(domMessage); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifySmsDelivery(int32_t aId, - int32_t aDeliveryStatus, - jni::String::Param aReceiver, - jni::String::Param aBody, - int64_t aTimestamp) -{ - // TODO Need to add the message `messageClass` parameter value. Bug 804476 - // TODO Need to correct the message `threadId` parameter value. Bug 859098 - SmsMessageData message; - message.id() = aId; - message.threadId() = 0; - message.iccId() = EmptyString(); - message.delivery() = eDeliveryState_Sent; - message.deliveryStatus() = static_cast(aDeliveryStatus); - message.sender() = EmptyString(); - message.receiver() = aReceiver ? nsString(aReceiver) : EmptyString(); - message.body() = aBody ? nsString(aBody) : EmptyString(); - message.messageClass() = eMessageClass_Normal; - message.timestamp() = aTimestamp; - message.sentTimestamp() = aTimestamp; - message.deliveryTimestamp() = aTimestamp; - message.read() = true; - - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr obs = services::GetObserverService(); - if (!obs) { - return; - } - - nsCOMPtr domMessage = new SmsMessageInternal(message); - const char* topic = (message.deliveryStatus() == eDeliveryStatus_Success) - ? kSmsDeliverySuccessObserverTopic - : kSmsDeliveryErrorObserverTopic; - obs->NotifyObservers(domMessage, topic, nullptr); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifySmsSendFailed(int32_t aError, int32_t aRequestId) -{ - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId); - if(!request) { - return; - } - - request->NotifySendMessageFailed(aError, nullptr); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifyGetSms(int32_t aId, - int32_t aDeliveryStatus, - jni::String::Param aReceiver, - jni::String::Param aSender, - jni::String::Param aBody, - int64_t aTimestamp, - bool aRead, - int32_t aRequestId) -{ - nsString receiver(aReceiver); - DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received - : eDeliveryState_Sent; - - // TODO Need to add the message `messageClass` parameter value. Bug 804476 - // TODO Need to correct the message `threadId` parameter value. Bug 859098 - SmsMessageData message; - message.id() = aId; - message.threadId() = 0; - message.iccId() = EmptyString(); - message.delivery() = state; - message.deliveryStatus() = static_cast(aDeliveryStatus); - message.sender() = aSender ? nsString(aSender) : EmptyString(); - message.receiver() = receiver; - message.body() = aBody ? nsString(aBody) : EmptyString(); - message.messageClass() = eMessageClass_Normal; - message.timestamp() = aTimestamp; - message.sentTimestamp() = aTimestamp; - message.deliveryTimestamp() = aTimestamp; - message.read() = aRead; - - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId); - if (!request) { - return; - } - - nsCOMPtr domMessage = new SmsMessageInternal(message); - request->NotifyMessageGot(domMessage); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifyGetSmsFailed(int32_t aError, int32_t aRequestId) -{ - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId); - if (!request) { - return; - } - - request->NotifyGetMessageFailed(aError); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifySmsDeleted(bool aDeleted, int32_t aRequestId) -{ - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId); - if (!request) { - return; - } - - // For android, we support only single SMS deletion. - bool deleted = aDeleted; - request->NotifyMessageDeleted(&deleted, 1); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifySmsDeleteFailed(int32_t aError, int32_t aRequestId) -{ - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId); - if (!request) { - return; - } - - request->NotifyDeleteMessageFailed(aError); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifyCursorError(int32_t aError, int32_t aRequestId) -{ - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsCursorRequest(aRequestId); - if (!request) { - return; - } - - request->NotifyCursorError(aError); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifyThreadCursorResult(int64_t aId, - jni::String::Param aLastMessageSubject, - jni::String::Param aBody, - int64_t aUnreadCount, - jni::ObjectArray::Param aParticipants, - int64_t aTimestamp, - jni::String::Param aLastMessageType, - int32_t aRequestId) -{ - ThreadData thread; - thread.id() = aId; - thread.lastMessageSubject() = aLastMessageSubject ? - nsString(aLastMessageSubject) : - EmptyString(); - thread.body() = aBody ? nsString(aBody) : EmptyString(); - thread.unreadCount() = aUnreadCount; - thread.timestamp() = aTimestamp; - thread.lastMessageType() = eMessageType_SMS; - - JNIEnv* const env = jni::GetEnvForThread(); - - jobjectArray participants = aParticipants.Get(); - jsize length = env->GetArrayLength(participants); - for (jsize i = 0; i < length; ++i) { - jstring participant = - static_cast(env->GetObjectArrayElement(participants, i)); - if (participant) { - thread.participants().AppendElement(nsJNIString(participant, env)); - } - } - - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->GetSmsCursorRequest(aRequestId); - if (!request) { - return; - } - - nsCOMArray arr; - arr.AppendElement(new MobileMessageThreadInternal(thread)); - - nsIMobileMessageThread** elements; - int32_t size; - size = arr.Forget(&elements); - - request->NotifyCursorResult(reinterpret_cast(elements), - size); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifyMessageCursorResult(int32_t aMessageId, - int32_t aDeliveryStatus, - jni::String::Param aReceiver, - jni::String::Param aSender, - jni::String::Param aBody, - int64_t aTimestamp, - int64_t aThreadId, - bool aRead, - int32_t aRequestId) -{ - nsString receiver = nsString(aReceiver); - DeliveryState state = receiver.IsEmpty() ? eDeliveryState_Received - : eDeliveryState_Sent; - - // TODO Need to add the message `messageClass` parameter value. Bug 804476 - SmsMessageData message; - message.id() = aMessageId; - message.threadId() = aThreadId; - message.iccId() = EmptyString(); - message.delivery() = state; - message.deliveryStatus() = static_cast(aDeliveryStatus); - message.sender() = aSender ? nsString(aSender) : EmptyString(); - message.receiver() = receiver; - message.body() = aBody ? nsString(aBody) : EmptyString(); - message.messageClass() = eMessageClass_Normal; - message.timestamp() = aTimestamp; - message.sentTimestamp() = aTimestamp; - message.deliveryTimestamp() = aTimestamp; - message.read() = aRead; - - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->GetSmsCursorRequest(aRequestId); - if (!request) { - return; - } - - nsCOMArray arr; - arr.AppendElement(new SmsMessageInternal(message)); - - nsISmsMessage** elements; - int32_t size; - size = arr.Forget(&elements); - - request->NotifyCursorResult(reinterpret_cast(elements), - size); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifyCursorDone(int32_t aRequestId) -{ - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsCursorRequest(aRequestId); - if (!request) { - return; - } - - request->NotifyCursorDone(); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifySmsMarkedAsRead(bool aMarkedAsRead, int32_t aRequestId) -{ - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId); - if (!request) { - return; - } - - request->NotifyMessageMarkedRead(aMarkedAsRead); - }); - NS_DispatchToMainThread(runnable); -} - -/*static*/ -void -SmsManager::NotifySmsMarkAsReadFailed(int32_t aError, int32_t aRequestId) -{ - nsCOMPtr runnable = NS_NewRunnableFunction([=]() { - nsCOMPtr request = - AndroidBridge::Bridge()->DequeueSmsRequest(aRequestId); - if (!request) { - return; - } - - request->NotifyMarkMessageReadFailed(aError); - }); - NS_DispatchToMainThread(runnable); -} - -} // namespace diff --git a/dom/mobilemessage/android/SmsManager.h b/dom/mobilemessage/android/SmsManager.h deleted file mode 100644 index e4ab1259d2..0000000000 --- a/dom/mobilemessage/android/SmsManager.h +++ /dev/null @@ -1,75 +0,0 @@ -/* -*- Mode: c++; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef SmsManager_h__ -#define SmsManager_h__ - -#include "GeneratedJNINatives.h" - -namespace mozilla { - -class SmsManager : public widget::GeckoSmsManager::Natives -{ -private: - SmsManager(); - -public: - static void NotifySmsReceived(jni::String::Param aSender, - jni::String::Param aBody, - int32_t aMessageClass, - int64_t aTimestamp); - static void NotifySmsSent(int32_t aId, - jni::String::Param aReceiver, - jni::String::Param aBody, - int64_t aTimestamp, - int32_t aRequestId); - static void NotifySmsDelivery(int32_t aId, - int32_t aDeliveryStatus, - jni::String::Param aReceiver, - jni::String::Param aBody, - int64_t aTimestamp); - static void NotifySmsSendFailed(int32_t aError, - int32_t aRequestId); - static void NotifyGetSms(int32_t aId, - int32_t aDeliveryStatus, - jni::String::Param aReceiver, - jni::String::Param aSender, - jni::String::Param aBody, - int64_t aTimestamp, - bool aRead, - int32_t aRequestId); - static void NotifyGetSmsFailed(int32_t aError, - int32_t aRequestId); - static void NotifySmsDeleted(bool aDeleted, - int32_t aRequestId); - static void NotifySmsDeleteFailed(int32_t aError, - int32_t aRequestId); - static void NotifyCursorError(int32_t aError, - int32_t aRequestId); - static void NotifyThreadCursorResult(int64_t aId, - jni::String::Param aLastMessageSubject, - jni::String::Param aBody, - int64_t aUnreadCount, - jni::ObjectArray::Param aParticipants, - int64_t aTimestamp, - jni::String::Param aLastMessageType, - int32_t aRequestId); - static void NotifyMessageCursorResult(int32_t aMessageId, - int32_t aDeliveryStatus, - jni::String::Param aReceiver, - jni::String::Param aSender, - jni::String::Param aBody, - int64_t aTimestamp, - int64_t aThreadId, - bool aRead, - int32_t aRequestId); - static void NotifyCursorDone(int32_t aRequestId); - static void NotifySmsMarkedAsRead(bool aMarkedAsRead, int32_t aRequestId); - static void NotifySmsMarkAsReadFailed(int32_t aError, int32_t aRequestId); -}; - -} // namespace - -#endif // SmsManager_h__ diff --git a/dom/mobilemessage/android/SmsService.cpp b/dom/mobilemessage/android/SmsService.cpp deleted file mode 100644 index 725c2cfeea..0000000000 --- a/dom/mobilemessage/android/SmsService.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "SmsService.h" -#include "AndroidBridge.h" - -namespace mozilla { -namespace dom { -namespace mobilemessage { - -NS_IMPL_ISUPPORTS(SmsService, nsISmsService) - -NS_IMETHODIMP -SmsService::GetSmsDefaultServiceId(uint32_t* aServiceId) -{ - // Android has no official DSDS support. - *aServiceId = 0; - return NS_OK; -} - -NS_IMETHODIMP -SmsService::GetSegmentInfoForText(const nsAString& aText, - nsIMobileMessageCallback* aRequest) -{ - if (!AndroidBridge::Bridge()) { - return NS_ERROR_FAILURE; - } - - nsresult rv = AndroidBridge::Bridge()->GetSegmentInfoForText(aText, aRequest); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -SmsService::Send(uint32_t aServiceId, - const nsAString& aNumber, - const nsAString& aMessage, - bool aSilent, - nsIMobileMessageCallback* aRequest) -{ - if (!AndroidBridge::Bridge()) { - return NS_OK; - } - - AndroidBridge::Bridge()->SendMessage(aNumber, aMessage, aRequest); - return NS_OK; -} - -NS_IMETHODIMP -SmsService::AddSilentNumber(const nsAString& aNumber) -{ - NS_NOTYETIMPLEMENTED("Implement me!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -SmsService::RemoveSilentNumber(const nsAString& aNumber) -{ - NS_NOTYETIMPLEMENTED("Implement me!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -SmsService::GetSmscAddress(uint32_t aServiceId, - nsIMobileMessageCallback *aRequest) -{ - // TODO: bug 878016 - Android backend: implement getSMSCAddress/setSMSCAddress - NS_NOTYETIMPLEMENTED("Implement me!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -NS_IMETHODIMP -SmsService::SetSmscAddress(uint32_t aServiceId, - const nsAString& aNumber, - uint32_t aTypeOfNumber, - uint32_t aNumberPlanIdentification, - nsIMobileMessageCallback* aRequest) -{ - // TODO: bug 878016 - Android backend: implement getSMSCAddress/setSMSCAddress - NS_NOTYETIMPLEMENTED("Implement me!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - -} // namespace mobilemessage -} // namespace dom -} // namespace mozilla diff --git a/dom/mobilemessage/android/SmsService.h b/dom/mobilemessage/android/SmsService.h deleted file mode 100644 index 0c5f04d84a..0000000000 --- a/dom/mobilemessage/android/SmsService.h +++ /dev/null @@ -1,30 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. 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_mobilemessage_SmsService_h -#define mozilla_dom_mobilemessage_SmsService_h - -#include "nsISmsService.h" - -namespace mozilla { -namespace dom { -namespace mobilemessage { - -class SmsService final : public nsISmsService -{ -private: - ~SmsService() {} - -public: - NS_DECL_ISUPPORTS - NS_DECL_NSISMSSERVICE -}; - -} // namespace mobilemessage -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_mobilemessage_SmsService_h diff --git a/dom/mobilemessage/gonk/MmsService.js b/dom/mobilemessage/gonk/MmsService.js index a58ea4eb6d..6dc46af901 100644 --- a/dom/mobilemessage/gonk/MmsService.js +++ b/dom/mobilemessage/gonk/MmsService.js @@ -36,7 +36,6 @@ const kSmsDeletedObserverTopic = "sms-deleted"; const NS_XPCOM_SHUTDOWN_OBSERVER_ID = "xpcom-shutdown"; const kNetworkConnStateChangedTopic = "network-connection-state-changed"; -const kPrefRilRadioDisabled = "ril.radio.disabled"; const kPrefMmsDebuggingEnabled = "mms.debugging.enabled"; // HTTP status codes: @@ -194,18 +193,12 @@ function getDefaultServiceId() { } /** - * Return Radio disabled state. + * Return radio disabled state. */ -function getRadioDisabledState() { - let state; - try { - state = Services.prefs.getBoolPref(kPrefRilRadioDisabled); - } catch (e) { - if (DEBUG) debug("Getting preference 'ril.radio.disabled' fails."); - state = false; - } - - return state; +function isRadioOff(aServiceId) { + let connection = gMobileConnectionService.getItemByServiceId(aServiceId); + return connection.radioState + !== Ci.nsIMobileConnection.MOBILE_RADIO_STATE_ENABLED; } /** @@ -422,7 +415,7 @@ MmsConnection.prototype = { this.pendingCallbacks.push(callback); let errorStatus; - if (getRadioDisabledState()) { + if (isRadioOff(this.serviceId)) { if (DEBUG) debug("Error! Radio is disabled when sending MMS."); errorStatus = _HTTP_STATUS_RADIO_DISABLED; } else if (this.getCardState() != Ci.nsIIcc.CARD_STATE_READY) { @@ -993,7 +986,8 @@ function CancellableTransaction(cancellableId, serviceId) { this.isCancelled = false; } CancellableTransaction.prototype = { - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, + Ci.nsIMobileConnectionListener]), // The timer for retrying sending or retrieving process. timer: null, @@ -1010,8 +1004,9 @@ CancellableTransaction.prototype = { if (!this.isObserversAdded) { Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); Services.obs.addObserver(this, kSmsDeletedObserverTopic, false); - Services.prefs.addObserver(kPrefRilRadioDisabled, this, false); Services.prefs.addObserver(kPrefDefaultServiceId, this, false); + gMobileConnectionService + .getItemByServiceId(this.serviceId).registerListener(this); this.isObserversAdded = true; } @@ -1023,8 +1018,9 @@ CancellableTransaction.prototype = { if (this.isObserversAdded) { Services.obs.removeObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); Services.obs.removeObserver(this, kSmsDeletedObserverTopic); - Services.prefs.removeObserver(kPrefRilRadioDisabled, this); Services.prefs.removeObserver(kPrefDefaultServiceId, this); + gMobileConnectionService + .getItemByServiceId(this.serviceId).unregisterListener(this); this.isObserversAdded = false; } }, @@ -1080,18 +1076,35 @@ CancellableTransaction.prototype = { break; } case NS_PREFBRANCH_PREFCHANGE_TOPIC_ID: { - if (data == kPrefRilRadioDisabled) { - if (getRadioDisabledState()) { - this.cancelRunning(_MMS_ERROR_RADIO_DISABLED); - } - } else if (data === kPrefDefaultServiceId && + if (data === kPrefDefaultServiceId && this.serviceId != getDefaultServiceId()) { this.cancelRunning(_MMS_ERROR_SIM_CARD_CHANGED); } break; } } - } + }, + + // nsIMobileConnectionListener + + notifyVoiceChanged: function() {}, + notifyDataChanged: function() {}, + notifyDataError: function(message) {}, + notifyCFStateChanged: function(action, reason, number, timeSeconds, serviceClass) {}, + notifyEmergencyCbModeChanged: function(active, timeoutMs) {}, + notifyOtaStatusChanged: function(status) {}, + + notifyRadioStateChanged: function() { + if (isRadioOff(this.serviceId)) { + this.cancelRunning(_MMS_ERROR_RADIO_DISABLED); + } + }, + + notifyClirModeChanged: function(mode) {}, + notifyLastKnownNetworkChanged: function() {}, + notifyLastKnownHomeNetworkChanged: function() {}, + notifyNetworkSelectionModeChanged: function() {}, + notifyDeviceIdentitiesChanged: function() {} }; /** @@ -2388,7 +2401,7 @@ MmsService.prototype = { } // Check radio state in prior to default service Id. - if (getRadioDisabledState()) { + if (isRadioOff(aServiceId)) { if (DEBUG) debug("Error! Radio is disabled when sending MMS."); sendTransactionCb(mmsMessage, Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR, @@ -2502,7 +2515,13 @@ MmsService.prototype = { // Hence, for manual retrieving, instead of checking radio state later // in MmsConnection.acquire(), We have to check radio state in prior to // iccId to return the error correctly. - if (getRadioDisabledState()) { + let numRadioInterfaces = gMobileConnectionService.numItems; + let isAllRadioOff = true; + for (let serviceId = 0; serviceId < numRadioInterfaces; serviceId++) { + isAllRadioOff &= isRadioOff(serviceId); + } + + if (isAllRadioOff) { if (DEBUG) debug("Error! Radio is disabled when retrieving MMS."); aRequest.notifyGetMessageFailed( Ci.nsIMobileMessageCallback.RADIO_DISABLED_ERROR); diff --git a/dom/mobilemessage/gonk/WspPduHelper.jsm b/dom/mobilemessage/gonk/WspPduHelper.jsm index 8b3ca49d41..0b26af95cc 100644 --- a/dom/mobilemessage/gonk/WspPduHelper.jsm +++ b/dom/mobilemessage/gonk/WspPduHelper.jsm @@ -7,6 +7,7 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.importGlobalProperties(['Blob']); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/wap_consts.js", this); var DEBUG; // set to true to see debug messages @@ -20,10 +21,21 @@ const HT = 9; const DQUOTE = 34; const DEL = 127; +XPCOMUtils.defineConstant(this, "NUL", NUL); +XPCOMUtils.defineConstant(this, "CR", CR); +XPCOMUtils.defineConstant(this, "LF", LF); +XPCOMUtils.defineConstant(this, "SP", SP); +XPCOMUtils.defineConstant(this, "HT", HT); +XPCOMUtils.defineConstant(this, "DQUOTE", DQUOTE); +XPCOMUtils.defineConstant(this, "DEL", DEL); + // Special ASCII character ranges const CTLS = 32; const ASCIIS = 128; +XPCOMUtils.defineConstant(this, "CTLS", CTLS); +XPCOMUtils.defineConstant(this, "ASCIIS", ASCIIS); + /** * Error class for generic encoding/decoding failures. */ diff --git a/dom/mobilemessage/moz.build b/dom/mobilemessage/moz.build index 41a559488e..8ec2f26660 100644 --- a/dom/mobilemessage/moz.build +++ b/dom/mobilemessage/moz.build @@ -16,13 +16,7 @@ EXPORTS.mozilla.dom.mobilemessage += [ 'Types.h', # Required by IPDL SmsTypes.h ] -if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': - SOURCES += [ - 'android/MobileMessageDatabaseService.cpp', - 'android/SmsManager.cpp', - 'android/SmsService.cpp', - ] -elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']: +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['MOZ_B2G_RIL']: EXTRA_JS_MODULES += [ 'gonk/mms_consts.js', 'gonk/MmsPduHelper.jsm', diff --git a/dom/mobilemessage/tests/marionette/head.js b/dom/mobilemessage/tests/marionette/head.js index eee60f1346..414199ceba 100644 --- a/dom/mobilemessage/tests/marionette/head.js +++ b/dom/mobilemessage/tests/marionette/head.js @@ -48,7 +48,7 @@ function pushPrefEnv(aPrefs) { * * @return A deferred promise. */ -let manager; +var manager; function ensureMobileMessage() { let deferred = Promise.defer(); @@ -417,7 +417,7 @@ function deleteAllMessages() { return getAllMessages().then(deleteMessages); } -let pendingEmulatorCmdCount = 0; +var pendingEmulatorCmdCount = 0; /** * Send emulator command with safe guard. diff --git a/dom/mobilemessage/tests/marionette/mmdb_head.js b/dom/mobilemessage/tests/marionette/mmdb_head.js index 7ea807886d..41920a8bd8 100644 --- a/dom/mobilemessage/tests/marionette/mmdb_head.js +++ b/dom/mobilemessage/tests/marionette/mmdb_head.js @@ -3,13 +3,13 @@ MARIONETTE_CONTEXT = "chrome"; -let Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise; +var Promise = Cu.import("resource://gre/modules/Promise.jsm").Promise; /** * Name space for MobileMessageDB.jsm. Only initialized after first call to * newMobileMessageDB. */ -let MMDB; +var MMDB; /** * Create a new MobileMessageDB instance. @@ -359,7 +359,7 @@ function createThreadCursor(aMmdb) { } // A reference to a nsIUUIDGenerator service. -let _uuidGenerator; +var _uuidGenerator; /** * Generate a new UUID. diff --git a/dom/mobilemessage/tests/marionette/test_bug814761.js b/dom/mobilemessage/tests/marionette/test_bug814761.js index 8cdcb6f431..36d71d7a2a 100644 --- a/dom/mobilemessage/tests/marionette/test_bug814761.js +++ b/dom/mobilemessage/tests/marionette/test_bug814761.js @@ -6,7 +6,7 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.setBoolPref("dom.sms.enabled", true); SpecialPowers.addPermission("sms", true, document); -let manager = window.navigator.mozMobileMessage; +var manager = window.navigator.mozMobileMessage; ok(manager instanceof MozMobileMessageManager, "manager is instance of " + manager.constructor); @@ -15,11 +15,11 @@ ok(manager instanceof MozMobileMessageManager, // the actual SMS is never received, so script will timeout waiting for the // onreceived event. Also note that a single larger message (i.e. 1600 // characters) works; so it is not a compounded send limit. -let fromNumber = "5551110000"; -let msgLength = 379; -let msgText = new Array(msgLength + 1).join('a'); +var fromNumber = "5551110000"; +var msgLength = 379; +var msgText = new Array(msgLength + 1).join('a'); -let pendingEmulatorCmdCount = 0; +var pendingEmulatorCmdCount = 0; function sendSmsToEmulator(from, text) { ++pendingEmulatorCmdCount; diff --git a/dom/mobilemessage/tests/marionette/test_error_of_mms_manual_retrieval.js b/dom/mobilemessage/tests/marionette/test_error_of_mms_manual_retrieval.js index 3ba2f00876..04c588533c 100644 --- a/dom/mobilemessage/tests/marionette/test_error_of_mms_manual_retrieval.js +++ b/dom/mobilemessage/tests/marionette/test_error_of_mms_manual_retrieval.js @@ -9,18 +9,17 @@ MARIONETTE_CONTEXT = "chrome"; Cu.import("resource://gre/modules/Promise.jsm"); -let MMS = {}; +var MMS = {}; Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS); -let gMobileMessageDatabaseService = +var gMobileMessageDatabaseService = Cc["@mozilla.org/mobilemessage/gonkmobilemessagedatabaseservice;1"] .getService(Ci.nsIGonkMobileMessageDatabaseService); -let gUuidGenerator = - Cc["@mozilla.org/uuid-generator;1"] - .getService(Ci.nsIUUIDGenerator); +var gUuidGenerator = + Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator); -let gMmsService = Cc["@mozilla.org/mms/gonkmmsservice;1"] +var gMmsService = Cc["@mozilla.org/mms/gonkmmsservice;1"] .getService(Ci.nsIMmsService); function saveMmsNotification() { diff --git a/dom/mobilemessage/tests/marionette/test_filter_mixed.js b/dom/mobilemessage/tests/marionette/test_filter_mixed.js index cac2d6e4e6..f53809b40d 100644 --- a/dom/mobilemessage/tests/marionette/test_filter_mixed.js +++ b/dom/mobilemessage/tests/marionette/test_filter_mixed.js @@ -9,11 +9,11 @@ const NUM_THREADS = 10; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let manager = window.navigator.mozMobileMessage; +var manager = window.navigator.mozMobileMessage; ok(manager instanceof MozMobileMessageManager, "manager is instance of " + manager.constructor); -let pendingEmulatorCmdCount = 0; +var pendingEmulatorCmdCount = 0; function sendSmsToEmulator(from, text) { ++pendingEmulatorCmdCount; @@ -25,7 +25,7 @@ function sendSmsToEmulator(from, text) { }); } -let tasks = { +var tasks = { // List of test fuctions. Each of them should call |tasks.next()| when // completed or |tasks.finish()| to jump to the last one. _tasks: [], @@ -117,7 +117,7 @@ tasks.push(deleteAllMessages); * send to "+15555315559" * receive from "5555315559", count = 10 */ -let threadIds = []; +var threadIds = []; tasks.push(function populateMessages() { let count = 0; @@ -167,10 +167,10 @@ tasks.push(function populateMessages() { sendMessage(count); }); -let INVALID_NUMBER = "12345"; -let INVALID_NUMBER2 = "6789"; +var INVALID_NUMBER = "12345"; +var INVALID_NUMBER2 = "6789"; -let INVALID_THREAD_ID; +var INVALID_THREAD_ID; tasks.push(function assignInvalidThreadID() { INVALID_THREAD_ID = threadIds[threadIds.length - 1] + 1; log("Set INVALID_THREAD_ID to be " + INVALID_THREAD_ID); diff --git a/dom/mobilemessage/tests/marionette/test_filter_read.js b/dom/mobilemessage/tests/marionette/test_filter_read.js index cbe67c6241..fc5d912f5a 100644 --- a/dom/mobilemessage/tests/marionette/test_filter_read.js +++ b/dom/mobilemessage/tests/marionette/test_filter_read.js @@ -6,9 +6,9 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let manager = window.navigator.mozMobileMessage; -let numberMsgs = 10; -let smsList = new Array(); +var manager = window.navigator.mozMobileMessage; +var numberMsgs = 10; +var smsList = new Array(); function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_filter_received.js b/dom/mobilemessage/tests/marionette/test_filter_received.js index 6e1d4da2e9..0297b1ed8c 100644 --- a/dom/mobilemessage/tests/marionette/test_filter_received.js +++ b/dom/mobilemessage/tests/marionette/test_filter_received.js @@ -6,9 +6,9 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let manager = window.navigator.mozMobileMessage; -let numberMsgs = 10; -let smsList = new Array(); +var manager = window.navigator.mozMobileMessage; +var numberMsgs = 10; +var smsList = new Array(); function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_filter_sent.js b/dom/mobilemessage/tests/marionette/test_filter_sent.js index e4d6f07d3b..625f58751f 100644 --- a/dom/mobilemessage/tests/marionette/test_filter_sent.js +++ b/dom/mobilemessage/tests/marionette/test_filter_sent.js @@ -6,9 +6,9 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let manager = window.navigator.mozMobileMessage; -let numberMsgs = 10; -let smsList = new Array(); +var manager = window.navigator.mozMobileMessage; +var numberMsgs = 10; +var smsList = new Array(); function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_filter_unread.js b/dom/mobilemessage/tests/marionette/test_filter_unread.js index 1a4afad1a3..632c793458 100644 --- a/dom/mobilemessage/tests/marionette/test_filter_unread.js +++ b/dom/mobilemessage/tests/marionette/test_filter_unread.js @@ -6,9 +6,9 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let manager = window.navigator.mozMobileMessage; -let numberMsgs = 10; -let smsList = new Array(); +var manager = window.navigator.mozMobileMessage; +var numberMsgs = 10; +var smsList = new Array(); function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_incoming_delete.js b/dom/mobilemessage/tests/marionette/test_incoming_delete.js index 1a04064a1f..d0944fb396 100644 --- a/dom/mobilemessage/tests/marionette/test_incoming_delete.js +++ b/dom/mobilemessage/tests/marionette/test_incoming_delete.js @@ -9,8 +9,8 @@ SpecialPowers.addPermission("sms", true, document); const SENDER = "5555552368"; // the remote number const RECEIVER = "15555215554"; // the emulator's number -let manager = window.navigator.mozMobileMessage; -let msgText = "Mozilla Firefox OS!"; +var manager = window.navigator.mozMobileMessage; +var msgText = "Mozilla Firefox OS!"; function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_incoming_max_segments.js b/dom/mobilemessage/tests/marionette/test_incoming_max_segments.js index 737750b258..91b97bdc0f 100644 --- a/dom/mobilemessage/tests/marionette/test_incoming_max_segments.js +++ b/dom/mobilemessage/tests/marionette/test_incoming_max_segments.js @@ -6,10 +6,10 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.setBoolPref("dom.sms.enabled", true); SpecialPowers.addPermission("sms", true, document); -let manager = window.navigator.mozMobileMessage; +var manager = window.navigator.mozMobileMessage; // https://developer.mozilla.org/en-US/docs/DOM/SmsManager -let maxCharsPerSms = 160; -let maxSegments = 10; // 10 message segments concatenated into 1 multipart SMS +var maxCharsPerSms = 160; +var maxSegments = 10; // 10 message segments concatenated into 1 multipart SMS const REMOTE = "5551234567"; // the remote number const EMULATOR = "15555215554"; // the emulator's number diff --git a/dom/mobilemessage/tests/marionette/test_invalid_address.js b/dom/mobilemessage/tests/marionette/test_invalid_address.js index 5464c34d77..1210d46698 100644 --- a/dom/mobilemessage/tests/marionette/test_invalid_address.js +++ b/dom/mobilemessage/tests/marionette/test_invalid_address.js @@ -8,7 +8,7 @@ const MMS_MAX_LENGTH_SUBJECT = 40; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let tasks = { +var tasks = { // List of test fuctions. Each of them should call |tasks.next()| when // completed or |tasks.finish()| to jump to the last one. _tasks: [], @@ -41,7 +41,7 @@ let tasks = { } }; -let manager; +var manager; function getAllMessages(callback, filter, reverse) { let messages = []; diff --git a/dom/mobilemessage/tests/marionette/test_mark_msg_read.js b/dom/mobilemessage/tests/marionette/test_mark_msg_read.js index ae41373c80..ee4386b80c 100644 --- a/dom/mobilemessage/tests/marionette/test_mark_msg_read.js +++ b/dom/mobilemessage/tests/marionette/test_mark_msg_read.js @@ -6,8 +6,8 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let manager = window.navigator.mozMobileMessage; -let smsList = new Array(); +var manager = window.navigator.mozMobileMessage; +var smsList = new Array(); function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_mark_msg_read_error.js b/dom/mobilemessage/tests/marionette/test_mark_msg_read_error.js index 5d6bbe85e9..772c8fdf14 100644 --- a/dom/mobilemessage/tests/marionette/test_mark_msg_read_error.js +++ b/dom/mobilemessage/tests/marionette/test_mark_msg_read_error.js @@ -6,8 +6,8 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let manager = window.navigator.mozMobileMessage; -let smsId; +var manager = window.navigator.mozMobileMessage; +var smsId; function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_message_classes.js b/dom/mobilemessage/tests/marionette/test_message_classes.js index 65724ae892..99ccf335d8 100644 --- a/dom/mobilemessage/tests/marionette/test_message_classes.js +++ b/dom/mobilemessage/tests/marionette/test_message_classes.js @@ -17,11 +17,11 @@ const SENT_TIMESTAMP = Date.UTC(2000, 0, 1); // Must be equal to PDU_TIMESTAMP. SpecialPowers.addPermission("sms", true, document); -let manager = window.navigator.mozMobileMessage; +var manager = window.navigator.mozMobileMessage; ok(manager instanceof MozMobileMessageManager, "manager is instance of " + manager.constructor); -let pendingEmulatorCmdCount = 0; +var pendingEmulatorCmdCount = 0; function sendSmsPduToEmulator(pdu) { ++pendingEmulatorCmdCount; diff --git a/dom/mobilemessage/tests/marionette/test_mmdb_foreachmatchedmmsdeliveryinfo.js b/dom/mobilemessage/tests/marionette/test_mmdb_foreachmatchedmmsdeliveryinfo.js index 03f896bf94..86fca946cf 100644 --- a/dom/mobilemessage/tests/marionette/test_mmdb_foreachmatchedmmsdeliveryinfo.js +++ b/dom/mobilemessage/tests/marionette/test_mmdb_foreachmatchedmmsdeliveryinfo.js @@ -13,7 +13,7 @@ const PHONE_2_NET = "5555215502"; const PHONE_3 = "+15555215503"; const PHONE_3_NET = "5555215503"; const EMAIL_1 = "foo@bar.com"; -let deliveryInfo = [ +var deliveryInfo = [ { receiver: PHONE_1 }, { receiver: PHONE_2 }, { receiver: PHONE_1 }, diff --git a/dom/mobilemessage/tests/marionette/test_mmdb_new.js b/dom/mobilemessage/tests/marionette/test_mmdb_new.js index 7417e94b3e..75c72d792a 100644 --- a/dom/mobilemessage/tests/marionette/test_mmdb_new.js +++ b/dom/mobilemessage/tests/marionette/test_mmdb_new.js @@ -5,7 +5,7 @@ MARIONETTE_TIMEOUT = 60000; MARIONETTE_HEAD_JS = 'mmdb_head.js'; const DBNAME = "test_mmdb_new:" + newUUID(); -let dbVersion = 0; +var dbVersion = 0; function check(aMmdb) { is(aMmdb.dbName, DBNAME, "dbName"); diff --git a/dom/mobilemessage/tests/marionette/test_mmdb_upgradeSchema_22.js b/dom/mobilemessage/tests/marionette/test_mmdb_upgradeSchema_22.js index af9c16f8ca..f647641587 100644 --- a/dom/mobilemessage/tests/marionette/test_mmdb_upgradeSchema_22.js +++ b/dom/mobilemessage/tests/marionette/test_mmdb_upgradeSchema_22.js @@ -6,10 +6,10 @@ MARIONETTE_HEAD_JS = 'mmdb_head.js'; Cu.import("resource://gre/modules/PhoneNumberUtils.jsm"); -let RIL = {}; +var RIL = {}; Cu.import("resource://gre/modules/ril_consts.js", RIL); -let MMS = {}; +var MMS = {}; Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS); const DBNAME = "test_mmdb_upgradeSchema_22:" + newUUID(); @@ -40,7 +40,7 @@ const FILTER_READ_READ = 1; const DISABLE_MMS_GROUPING_FOR_RECEIVING = true; -let LEGACY = { +var LEGACY = { saveRecord: function(aMessageRecord, aAddresses, aCallback) { if (DEBUG) debug("Going to store " + JSON.stringify(aMessageRecord)); diff --git a/dom/mobilemessage/tests/marionette/test_mmsmessage_attachments.js b/dom/mobilemessage/tests/marionette/test_mmsmessage_attachments.js index 8db959c98e..10012b469b 100644 --- a/dom/mobilemessage/tests/marionette/test_mmsmessage_attachments.js +++ b/dom/mobilemessage/tests/marionette/test_mmsmessage_attachments.js @@ -8,7 +8,7 @@ const MMS_MAX_LENGTH_SUBJECT = 40; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let tasks = { +var tasks = { // List of test fuctions. Each of them should call |tasks.next()| when // completed or |tasks.finish()| to jump to the last one. _tasks: [], @@ -41,7 +41,7 @@ let tasks = { } }; -let manager; +var manager; function getAllMessages(callback, filter, reverse) { let messages = []; diff --git a/dom/mobilemessage/tests/marionette/test_outgoing_delete.js b/dom/mobilemessage/tests/marionette/test_outgoing_delete.js index 3b9177ecde..0054f59f56 100644 --- a/dom/mobilemessage/tests/marionette/test_outgoing_delete.js +++ b/dom/mobilemessage/tests/marionette/test_outgoing_delete.js @@ -10,10 +10,10 @@ SpecialPowers.addPermission("sms", true, document); const SENDER = "15555215554"; // the emulator's number const RECEIVER = "5551117777"; // the destination number -let manager = window.navigator.mozMobileMessage; -let msgText = "Mozilla Firefox OS!"; -let gotSmsOnsent = false; -let gotReqOnsuccess = false; +var manager = window.navigator.mozMobileMessage; +var msgText = "Mozilla Firefox OS!"; +var gotSmsOnsent = false; +var gotReqOnsuccess = false; function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_outgoing_max_segments.js b/dom/mobilemessage/tests/marionette/test_outgoing_max_segments.js index 5ea9219a5f..a1da9e0d8d 100644 --- a/dom/mobilemessage/tests/marionette/test_outgoing_max_segments.js +++ b/dom/mobilemessage/tests/marionette/test_outgoing_max_segments.js @@ -6,10 +6,10 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.setBoolPref("dom.sms.enabled", true); SpecialPowers.addPermission("sms", true, document); -let manager = window.navigator.mozMobileMessage; +var manager = window.navigator.mozMobileMessage; // https://developer.mozilla.org/en-US/docs/DOM/SmsManager -let maxCharsPerSms = 160; -let maxSegments = 10; // 10 message segments concatenated into 1 multipart SMS +var maxCharsPerSms = 160; +var maxSegments = 10; // 10 message segments concatenated into 1 multipart SMS function verifyInitialState() { log("Verifying initial state."); diff --git a/dom/mobilemessage/tests/marionette/test_phone_number_normalization.js b/dom/mobilemessage/tests/marionette/test_phone_number_normalization.js index b2785a1e7c..d97369ac41 100644 --- a/dom/mobilemessage/tests/marionette/test_phone_number_normalization.js +++ b/dom/mobilemessage/tests/marionette/test_phone_number_normalization.js @@ -6,7 +6,7 @@ MARIONETTE_TIMEOUT = 60000; SpecialPowers.setBoolPref("dom.sms.enabled", true); SpecialPowers.addPermission("sms", true, document); -let pendingEmulatorCmdCount = 0; +var pendingEmulatorCmdCount = 0; function sendSmsToEmulator(from, text) { ++pendingEmulatorCmdCount; @@ -18,7 +18,7 @@ function sendSmsToEmulator(from, text) { }); } -let tasks = { +var tasks = { // List of test fuctions. Each of them should call |tasks.next()| when // completed or |tasks.finish()| to jump to the last one. _tasks: [], @@ -105,7 +105,7 @@ function validate(number, normalizedNumber) { }; } -let manager = window.navigator.mozMobileMessage; +var manager = window.navigator.mozMobileMessage; tasks.push(function() { log("Verifying initial state."); ok(manager instanceof MozMobileMessageManager, diff --git a/dom/mobilemessage/tests/marionette/test_strict_7bit_encoding.js b/dom/mobilemessage/tests/marionette/test_strict_7bit_encoding.js index 1ca79f2343..eedf681b7a 100644 --- a/dom/mobilemessage/tests/marionette/test_strict_7bit_encoding.js +++ b/dom/mobilemessage/tests/marionette/test_strict_7bit_encoding.js @@ -127,11 +127,11 @@ const SELF = "5554"; SpecialPowers.setBoolPref("dom.sms.enabled", true); SpecialPowers.addPermission("sms", true, document); -let manager = window.navigator.mozMobileMessage; +var manager = window.navigator.mozMobileMessage; ok(manager instanceof MozMobileMessageManager, "manager is instance of " + manager.constructor); -let tasks = { +var tasks = { // List of test fuctions. Each of them should call |tasks.next()| when // completed or |tasks.finish()| to jump to the last one. _tasks: [], diff --git a/dom/mobilemessage/tests/marionette/test_update_thread_record_in_delete.js b/dom/mobilemessage/tests/marionette/test_update_thread_record_in_delete.js index a11710e91b..3a770cc356 100644 --- a/dom/mobilemessage/tests/marionette/test_update_thread_record_in_delete.js +++ b/dom/mobilemessage/tests/marionette/test_update_thread_record_in_delete.js @@ -10,11 +10,11 @@ const MSGS = 3; SpecialPowers.addPermission("sms", true, document); SpecialPowers.setBoolPref("dom.sms.enabled", true); -let manager = window.navigator.mozMobileMessage; +var manager = window.navigator.mozMobileMessage; ok(manager instanceof MozMobileMessageManager, "manager is instance of " + manager.constructor); -let pendingEmulatorCmdCount = 0; +var pendingEmulatorCmdCount = 0; function sendSmsToEmulator(from, text) { ++pendingEmulatorCmdCount; @@ -26,7 +26,7 @@ function sendSmsToEmulator(from, text) { }); } -let tasks = { +var tasks = { // List of test fuctions. Each of them should call |tasks.next()| when // completed or |tasks.finish()| to jump to the last one. _tasks: [], @@ -121,7 +121,7 @@ tasks.push(getAllThreads.bind(null, function(threads) { tasks.next(); })); -let gotMessagesCount = 0; +var gotMessagesCount = 0; tasks.push(function() { manager.onreceived = function() { ++gotMessagesCount; @@ -136,7 +136,7 @@ tasks.push(function() { tasks.next(); }); -let allMessages; +var allMessages; tasks.push(function waitAllMessageReceived() { if (gotMessagesCount != MSGS) { window.setTimeout(waitAllMessageReceived, 100); @@ -150,7 +150,7 @@ tasks.push(function waitAllMessageReceived() { }); -let originalThread; +var originalThread; tasks.push(getAllThreads.bind(null, function(threads) { is(threads.length, 1, "Should have only one thread"); diff --git a/dom/mobilemessage/tests/xpcshell/header_helpers.js b/dom/mobilemessage/tests/xpcshell/header_helpers.js index 1c6ab6f596..c9589a09b0 100644 --- a/dom/mobilemessage/tests/xpcshell/header_helpers.js +++ b/dom/mobilemessage/tests/xpcshell/header_helpers.js @@ -3,10 +3,10 @@ "use strict"; -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; +var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -let subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] +var subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] .getService(Ci.mozIJSSubScriptLoader); /** diff --git a/dom/mobilemessage/tests/xpcshell/test_mms_pdu_helper.js b/dom/mobilemessage/tests/xpcshell/test_mms_pdu_helper.js index 6237fbf871..4d723bc74c 100644 --- a/dom/mobilemessage/tests/xpcshell/test_mms_pdu_helper.js +++ b/dom/mobilemessage/tests/xpcshell/test_mms_pdu_helper.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -let MMS = {}; +var MMS = {}; subscriptLoader.loadSubScript("resource://gre/modules/MmsPduHelper.jsm", MMS); MMS.debug = do_print; diff --git a/dom/mobilemessage/tests/xpcshell/test_mms_service.js b/dom/mobilemessage/tests/xpcshell/test_mms_service.js index 99aef7977d..4ddf35b43c 100644 --- a/dom/mobilemessage/tests/xpcshell/test_mms_service.js +++ b/dom/mobilemessage/tests/xpcshell/test_mms_service.js @@ -7,7 +7,7 @@ function newMmsTransactionHelper() { MMS_Service.debug = do_print; return MMS_Service.gMmsTransactionHelper; } -let CallFunc = newMmsTransactionHelper(); +var CallFunc = newMmsTransactionHelper(); function run_test() { run_next_test(); } @@ -136,4 +136,4 @@ add_test(function test_NameParameterInContentType() { do_check_eq(CallFunc.checkMaxValuesParameters(msg), false); run_next_test(); -}); \ No newline at end of file +}); diff --git a/dom/mobilemessage/tests/xpcshell/test_smsservice_createsmsmessage.js b/dom/mobilemessage/tests/xpcshell/test_smsservice_createsmsmessage.js index 5b2d7ddbbb..4c1b7c0c30 100644 --- a/dom/mobilemessage/tests/xpcshell/test_smsservice_createsmsmessage.js +++ b/dom/mobilemessage/tests/xpcshell/test_smsservice_createsmsmessage.js @@ -18,7 +18,7 @@ function do_check_throws(f, result, stack) { do_throw("expected result " + result + ", none thrown", stack); } -let gMobileMessageService = Cc["@mozilla.org/mobilemessage/mobilemessageservice;1"] +var gMobileMessageService = Cc["@mozilla.org/mobilemessage/mobilemessageservice;1"] .getService(Ci.nsIMobileMessageService); function newMessage() { return gMobileMessageService.createSmsMessage.apply(gMobileMessageService, arguments); diff --git a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper.js b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper.js index 3c348a9ce2..05388a3539 100644 --- a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper.js +++ b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -let WSP = {}; +var WSP = {}; subscriptLoader.loadSubScript("resource://gre/modules/WspPduHelper.jsm", WSP); WSP.debug = do_print; diff --git a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_header.js b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_header.js index 028d4847cd..5b4871815f 100644 --- a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_header.js +++ b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_header.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -let WSP = {}; +var WSP = {}; subscriptLoader.loadSubScript("resource://gre/modules/WspPduHelper.jsm", WSP); WSP.debug = do_print; diff --git a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_numeric.js b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_numeric.js index 6555c6faf3..defdf1e6e7 100644 --- a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_numeric.js +++ b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_numeric.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -let WSP = {}; +var WSP = {}; subscriptLoader.loadSubScript("resource://gre/modules/WspPduHelper.jsm", WSP); WSP.debug = do_print; diff --git a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_parameter.js b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_parameter.js index cc8dad85c0..d4d92eabf6 100644 --- a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_parameter.js +++ b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_parameter.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -let WSP = {}; +var WSP = {}; subscriptLoader.loadSubScript("resource://gre/modules/WspPduHelper.jsm", WSP); WSP.debug = do_print; diff --git a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_text.js b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_text.js index 3e91e582c0..e81c3c15e8 100644 --- a/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_text.js +++ b/dom/mobilemessage/tests/xpcshell/test_wsp_pdu_helper_text.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -let WSP = {}; +var WSP = {}; subscriptLoader.loadSubScript("resource://gre/modules/WspPduHelper.jsm", WSP); WSP.debug = do_print; @@ -81,7 +81,7 @@ add_test(function test_NullTerminatedTexts_encode() { // Test target: Token // -let TOKEN_SEPS = "()<>@,;:\\\"/[]?={} \t"; +var TOKEN_SEPS = "()<>@,;:\\\"/[]?={} \t"; //// Token.decode //// diff --git a/dom/network/tests/mochitest.ini b/dom/network/tests/mochitest.ini index 41423a7136..4ecc8f452e 100644 --- a/dom/network/tests/mochitest.ini +++ b/dom/network/tests/mochitest.ini @@ -12,7 +12,7 @@ skip-if = toolkit == "gonk" [test_tcpsocket_enabled_no_perm.html] skip-if = toolkit == "gonk" [test_tcpsocket_enabled_with_perm.html] -skip-if = toolkit == "gonk" || e10s +skip-if = toolkit == "gonk" [test_tcpsocket_legacy.html] [test_networkstats_alarms.html] skip-if = toolkit != "gonk" @@ -24,4 +24,4 @@ skip-if = toolkit != "gonk" skip-if = toolkit != "gonk" [test_networkstats_enabled_perm.html] skip-if = toolkit != "gonk" -[test_udpsocket.html] \ No newline at end of file +[test_udpsocket.html] diff --git a/dom/network/tests/test_tcpsocket_enabled_with_perm.html b/dom/network/tests/test_tcpsocket_enabled_with_perm.html index 65dcfe339c..44999fdfe8 100644 --- a/dom/network/tests/test_tcpsocket_enabled_with_perm.html +++ b/dom/network/tests/test_tcpsocket_enabled_with_perm.html @@ -17,10 +17,11 @@ the tcp-socket permission has been granted. **/ SimpleTest.waitForExplicitFinish(); -SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, runTest); -function runTest() { - SpecialPowers.addPermission("tcp-socket", true, document); +SpecialPowers.pushPrefEnv({"set": [['dom.mozTCPSocket.enabled', true]]}, function() { + SpecialPowers.pushPermissions([{type: "tcp-socket", allow: true, context: document}], runTest); +}); +function runTest() { ok('TCPSocket' in this, "TCPSocket should be accessible if dom.mozTCPSocket.enabled is true"); ok(new TCPSocket('localhost', 80), "TCPSocket constructor should work for content that has the tcp-socket permission"); diff --git a/dom/network/tests/unit_stats/test_networkstats_db.js b/dom/network/tests/unit_stats/test_networkstats_db.js index 9e7778d2ac..50a5dc1867 100644 --- a/dom/network/tests/unit_stats/test_networkstats_db.js +++ b/dom/network/tests/unit_stats/test_networkstats_db.js @@ -1,7 +1,7 @@ /* Any: copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; +var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/NetworkStatsDB.jsm"); diff --git a/dom/network/tests/unit_stats/test_networkstats_service.js b/dom/network/tests/unit_stats/test_networkstats_service.js index e0bf49ea1a..8c43a9b54b 100644 --- a/dom/network/tests/unit_stats/test_networkstats_service.js +++ b/dom/network/tests/unit_stats/test_networkstats_service.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; +var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; const NETWORK_STATUS_READY = 0; const NETWORK_STATUS_STANDBY = 1; diff --git a/dom/network/tests/unit_stats/test_networkstats_service_proxy.js b/dom/network/tests/unit_stats/test_networkstats_service_proxy.js index b78c53f173..131b886d03 100644 --- a/dom/network/tests/unit_stats/test_networkstats_service_proxy.js +++ b/dom/network/tests/unit_stats/test_networkstats_service_proxy.js @@ -1,7 +1,7 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; +var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); diff --git a/dom/notification/Notification.cpp b/dom/notification/Notification.cpp index 563e7b6f0e..995376e09f 100644 --- a/dom/notification/Notification.cpp +++ b/dom/notification/Notification.cpp @@ -544,7 +544,8 @@ protected: uint32_t Notification::sCount = 0; -NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow, mPromise) +NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow, mPromise, + mCallback) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest) NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest) @@ -615,7 +616,11 @@ NotificationPermissionRequest::GetElement(nsIDOMElement** aElement) NS_IMETHODIMP NotificationPermissionRequest::Cancel() { - mPermission = NotificationPermission::Denied; + // `Cancel` is called if the user denied permission or dismissed the + // permission request. To distinguish between the two, we set the + // permission to "default" and query the permission manager in + // `ResolvePromise`. + mPermission = NotificationPermission::Default; return DispatchResolvePromise(); } @@ -650,6 +655,11 @@ nsresult NotificationPermissionRequest::ResolvePromise() { nsresult rv = NS_OK; + if (mPermission == NotificationPermission::Default) { + // This will still be "default" if the user dismissed the doorhanger, + // or "denied" otherwise. + mPermission = Notification::TestPermission(mPrincipal); + } if (mCallback) { ErrorResult error; mCallback->Call(mPermission, error); @@ -1285,8 +1295,18 @@ public: ServiceWorkerNotificationObserver(const nsAString& aScope, nsIPrincipal* aPrincipal, - const nsAString& aID) - : mScope(aScope), mID(aID), mPrincipal(aPrincipal) + const nsAString& aID, + const nsAString& aTitle, + const nsAString& aDir, + const nsAString& aLang, + const nsAString& aBody, + const nsAString& aTag, + const nsAString& aIcon, + const nsAString& aData, + const nsAString& aBehavior) + : mScope(aScope), mID(aID), mPrincipal(aPrincipal), mTitle(aTitle) + , mDir(aDir), mLang(aLang), mBody(aBody), mTag(aTag), mIcon(aIcon) + , mData(aData), mBehavior(aBehavior) { AssertIsOnMainThread(); MOZ_ASSERT(aPrincipal); @@ -1299,6 +1319,14 @@ private: const nsString mScope; const nsString mID; nsCOMPtr mPrincipal; + const nsString mTitle; + const nsString mDir; + const nsString mLang; + const nsString mBody; + const nsString mTag; + const nsString mIcon; + const nsString mData; + const nsString mBehavior; }; NS_IMPL_ISUPPORTS(ServiceWorkerNotificationObserver, nsIObserver) @@ -1555,34 +1583,14 @@ WorkerNotificationObserver::Observe(nsISupports* aSubject, const char* aTopic, return NS_OK; } -class NotificationClickEventCallback final : public nsINotificationStorageCallback +NS_IMETHODIMP +ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject, + const char* aTopic, + const char16_t* aData) { -public: - NS_DECL_ISUPPORTS - - NotificationClickEventCallback(nsIPrincipal* aPrincipal, - const nsAString& aScope) - : mPrincipal(aPrincipal), mScope(aScope) - { - MOZ_ASSERT(aPrincipal); - } - - NS_IMETHOD Handle(const nsAString& aID, - const nsAString& aTitle, - const nsAString& aDir, - const nsAString& aLang, - const nsAString& aBody, - const nsAString& aTag, - const nsAString& aIcon, - const nsAString& aData, - const nsAString& aBehavior, - const nsAString& aServiceWorkerRegistrationID) override - { - MOZ_ASSERT(!aID.IsEmpty()); - MOZ_ASSERT(mScope.Equals(aServiceWorkerRegistrationID)); - - AssertIsOnMainThread(); + AssertIsOnMainThread(); + if (!strcmp("alertclickcallback", aTopic)) { nsAutoCString originSuffix; nsresult rv = mPrincipal->GetOriginSuffix(originSuffix); if (NS_WARN_IF(NS_FAILED(rv))) { @@ -1595,62 +1603,31 @@ public: if (swm) { swm->SendNotificationClickEvent(originSuffix, NS_ConvertUTF16toUTF8(mScope), - aID, - aTitle, - aDir, - aLang, - aBody, - aTag, - aIcon, - aData, - aBehavior); + mID, + mTitle, + mDir, + mLang, + mBody, + mTag, + mIcon, + mData, + mBehavior); } return NS_OK; } - NS_IMETHOD Done() override - { - return NS_OK; - } + if (!strcmp("alertfinished", aTopic)) { + nsString origin; + nsresult rv = Notification::GetOrigin(mPrincipal, origin); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } -private: - ~NotificationClickEventCallback() - { - } - - nsCOMPtr mPrincipal; - nsString mScope; -}; - -NS_IMPL_ISUPPORTS(NotificationClickEventCallback, nsINotificationStorageCallback) - -NS_IMETHODIMP -ServiceWorkerNotificationObserver::Observe(nsISupports* aSubject, - const char* aTopic, - const char16_t* aData) -{ - AssertIsOnMainThread(); - // Persistent notifications only care about the click event. - if (!strcmp("alertclickcallback", aTopic)) { - nsresult rv; + // Remove closed or dismissed persistent notifications. nsCOMPtr notificationStorage = - do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID, &rv); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsCOMPtr callback = - new NotificationClickEventCallback(mPrincipal, mScope); - - nsAutoString origin; - rv = Notification::GetOrigin(mPrincipal, origin); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - rv = notificationStorage->GetByID(origin, mID, callback); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; + do_GetService(NS_NOTIFICATION_STORAGE_CONTRACTID); + if (notificationStorage) { + notificationStorage->Delete(origin, mID); } } @@ -1758,7 +1735,21 @@ Notification::ShowInternal() // at the end of this function. // // The observer is wholly owned by the NotificationObserver passed to the alert service. - observer = new ServiceWorkerNotificationObserver(mScope, GetPrincipal(), mID); + nsAutoString behavior; + if (NS_WARN_IF(!mBehavior.ToJSON(behavior))) { + behavior.Truncate(); + } + observer = new ServiceWorkerNotificationObserver(mScope, + GetPrincipal(), + mID, + mTitle, + DirectionToString(mDir), + mLang, + mBody, + mTag, + iconUrl, + mDataAsBase64, + behavior); } MOZ_ASSERT(observer); nsCOMPtr alertObserver = new NotificationObserver(observer, @@ -1948,10 +1939,21 @@ Notification::GetPermissionInternal(nsIPrincipal* aPrincipal, } } + return TestPermission(aPrincipal); +} + +/* static */ NotificationPermission +Notification::TestPermission(nsIPrincipal* aPrincipal) +{ + AssertIsOnMainThread(); + uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION; nsCOMPtr permissionManager = services::GetPermissionManager(); + if (!permissionManager) { + return NotificationPermission::Default; + } permissionManager->TestExactPermissionFromPrincipal(aPrincipal, "desktop-notification", diff --git a/dom/notification/Notification.h b/dom/notification/Notification.h index e98a394620..9f8ddb66db 100644 --- a/dom/notification/Notification.h +++ b/dom/notification/Notification.h @@ -318,6 +318,8 @@ public: static NotificationPermission GetPermissionInternal(nsIPrincipal* aPrincipal, ErrorResult& rv); + static NotificationPermission TestPermission(nsIPrincipal* aPrincipal); + bool DispatchClickEvent(); bool DispatchNotificationClickEvent(); diff --git a/dom/notification/moz.build b/dom/notification/moz.build index 70685229b4..0a94587e39 100644 --- a/dom/notification/moz.build +++ b/dom/notification/moz.build @@ -36,4 +36,5 @@ LOCAL_INCLUDES += [ '/dom/workers', ] +BROWSER_CHROME_MANIFESTS += ['test/browser/browser.ini'] XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini'] diff --git a/dom/notification/test/browser/browser.ini b/dom/notification/test/browser/browser.ini new file mode 100644 index 0000000000..8a357c1a15 --- /dev/null +++ b/dom/notification/test/browser/browser.ini @@ -0,0 +1,2 @@ +[browser_permission_dismiss.js] +support-files = notification.html diff --git a/dom/notification/test/browser/browser_permission_dismiss.js b/dom/notification/test/browser/browser_permission_dismiss.js new file mode 100644 index 0000000000..de655870bd --- /dev/null +++ b/dom/notification/test/browser/browser_permission_dismiss.js @@ -0,0 +1,113 @@ +"use strict"; + +const ORIGIN_URI = Services.io.newURI("http://mochi.test:8888", null, null); +const PERMISSION_NAME = "desktop-notification"; +const PROMPT_ALLOW_BUTTON = -1; +const PROMPT_BLOCK_BUTTON = 0; +const TEST_URL = "http://mochi.test:8888/browser/dom/notification/test/browser/notification.html"; + +/** + * Clicks the specified web-notifications prompt button. + * + * @param {Number} aButtonIndex Number indicating which button to click. + * See the constants in this file. + * @note modified from toolkit/components/passwordmgr/test/browser/head.js + */ +function clickDoorhangerButton(aButtonIndex) { + ok(true, "Looking for action at index " + aButtonIndex); + + let popup = PopupNotifications.getNotification("web-notifications"); + let notifications = popup.owner.panel.childNodes; + ok(notifications.length > 0, "at least one notification displayed"); + ok(true, notifications.length + " notification(s)"); + let notification = notifications[0]; + + if (aButtonIndex == -1) { + ok(true, "Triggering main action"); + notification.button.doCommand(); + } else if (aButtonIndex <= popup.secondaryActions.length) { + ok(true, "Triggering secondary action " + aButtonIndex); + notification.childNodes[aButtonIndex].doCommand(); + } +} + +/** + * Opens a tab which calls `Notification.requestPermission()` with a callback + * argument, calls the `task` function while the permission prompt is open, + * and verifies that the expected permission is set. + * + * @param {Function} task Task function to run to interact with the prompt. + * @param {String} permission Expected permission value. + * @return {Promise} resolving when the task function is done and the tab + * closes. + */ +function tabWithRequest(task, permission) { + Services.perms.remove(ORIGIN_URI, PERMISSION_NAME); + + return BrowserTestUtils.withNewTab({ + gBrowser, + url: TEST_URL, + }, function*(browser) { + let requestPromise = ContentTask.spawn(browser, { + permission + }, function*({permission}) { + function requestCallback(perm) { + is(perm, permission, + "Should call the legacy callback with the permission state"); + } + let perm = yield content.window.Notification + .requestPermission(requestCallback); + is(perm, permission, + "Should resolve the promise with the permission state"); + }); + + yield BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown"); + yield task(); + yield requestPromise; + }); +} + +add_task(function* setup() { + SimpleTest.registerCleanupFunction(() => { + Services.perms.remove(ORIGIN_URI, PERMISSION_NAME); + }); +}); + +add_task(function* test_requestPermission_granted() { + yield tabWithRequest(function() { + clickDoorhangerButton(PROMPT_ALLOW_BUTTON); + }, "granted"); + + ok(!PopupNotifications.getNotification("web-notifications"), + "Should remove the doorhanger notification icon if granted"); + + is(Services.perms.testPermission(ORIGIN_URI, PERMISSION_NAME), + Services.perms.ALLOW_ACTION, + "Check permission in perm. manager"); +}); + +add_task(function* test_requestPermission_denied() { + yield tabWithRequest(function() { + clickDoorhangerButton(PROMPT_BLOCK_BUTTON); + }, "denied"); + + ok(!PopupNotifications.getNotification("web-notifications"), + "Should remove the doorhanger notification icon if denied"); + + is(Services.perms.testPermission(ORIGIN_URI, PERMISSION_NAME), + Services.perms.DENY_ACTION, + "Check permission in perm. manager"); +}); + +add_task(function* test_requestPermission_dismissed() { + yield tabWithRequest(function() { + PopupNotifications.panel.hidePopup(); + }, "default"); + + ok(!PopupNotifications.getNotification("web-notifications"), + "Should remove the doorhanger notification icon if dismissed"); + + is(Services.perms.testPermission(ORIGIN_URI, PERMISSION_NAME), + Services.perms.UNKNOWN_ACTION, + "Check permission in perm. manager"); +}); diff --git a/dom/notification/test/browser/notification.html b/dom/notification/test/browser/notification.html new file mode 100644 index 0000000000..0ceeb8ea46 --- /dev/null +++ b/dom/notification/test/browser/notification.html @@ -0,0 +1,11 @@ + + + + + Notifications test + + + + + + diff --git a/dom/notification/test/unit/common_test_notificationdb.js b/dom/notification/test/unit/common_test_notificationdb.js index b692ef6fbe..83888edf45 100644 --- a/dom/notification/test/unit/common_test_notificationdb.js +++ b/dom/notification/test/unit/common_test_notificationdb.js @@ -1,6 +1,6 @@ "use strict"; -const Cu = Components.utils; +var Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -22,10 +22,10 @@ function getNotificationObject(app, id, tag) { }; } -let systemNotification = +var systemNotification = getNotificationObject("system", "{2bc883bf-2809-4432-b0f4-f54e10372764}"); -let calendarNotification = +var calendarNotification = getNotificationObject("calendar", "{d8d11299-a58e-429b-9a9a-57c562982fbf}"); // Helper to start the NotificationDB diff --git a/dom/offline/nsDOMOfflineResourceList.h b/dom/offline/nsDOMOfflineResourceList.h index d954a283f5..7c666b9bcf 100644 --- a/dom/offline/nsDOMOfflineResourceList.h +++ b/dom/offline/nsDOMOfflineResourceList.h @@ -116,7 +116,7 @@ public: } uint32_t Length() { - ErrorResult rv; + mozilla::IgnoredErrorResult rv; uint32_t length = GetMozLength(rv); return rv.Failed() ? 0 : length; } diff --git a/dom/permission/tests/mochitest.ini b/dom/permission/tests/mochitest.ini index ed586a0b02..ac1ae989c2 100644 --- a/dom/permission/tests/mochitest.ini +++ b/dom/permission/tests/mochitest.ini @@ -6,7 +6,7 @@ support-files = [test_alarms.html] [test_browser.html] [test_embed-apps.html] -skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage +skip-if = ((buildapp == 'b2g') && toolkit != 'gonk') || e10s #Bug 931116, b2g desktop specific, initial triage [test_idle.html] skip-if = (toolkit == 'gonk' && debug) #debug-only failure [test_permission_basics.html] diff --git a/dom/permission/tests/unit/test_bug808734.js b/dom/permission/tests/unit/test_bug808734.js index d2bc424edb..8dfbd9458b 100644 --- a/dom/permission/tests/unit/test_bug808734.js +++ b/dom/permission/tests/unit/test_bug808734.js @@ -1,4 +1,4 @@ -const Cu = Components.utils; +var Cu = Components.utils; const READWRITE = "readwrite"; const UNKNOWN = "foobar"; diff --git a/dom/plugins/test/crashtests/843086.xhtml b/dom/plugins/test/crashtests/843086.xhtml new file mode 100644 index 0000000000..a88e2193fc --- /dev/null +++ b/dom/plugins/test/crashtests/843086.xhtml @@ -0,0 +1 @@ + diff --git a/dom/plugins/test/crashtests/crashtests.list b/dom/plugins/test/crashtests/crashtests.list index fa9cb33484..aec2195f26 100644 --- a/dom/plugins/test/crashtests/crashtests.list +++ b/dom/plugins/test/crashtests/crashtests.list @@ -1,15 +1,14 @@ load 41276-1.html load 48856-1.html load 110650-1.html -skip-if(browserIsRemote||!haveTestPlugin||cocoaWidget) script 539897-1.html # browserIsRemote is bug XXXXXX -skip-if(browserIsRemote||!haveTestPlugin) script 540114-1.html # browserIsRemote is bug XXXXXX +skip-if(!haveTestPlugin) script 539897-1.html +asserts-if(winWidget&&browserIsRemote,0-1) skip-if(!haveTestPlugin) script 540114-1.html load 570884.html # This test relies on the reading of screenX/Y forcing a round trip to # the X server, which is a bad assumption for . # Plugin arch is going to change anyway with OOP content so skipping # this test for now is OK. -skip-if(browserIsRemote||!haveTestPlugin||http.platform!="X11"||!testPluginIsOOP()) load 598862.html - -# SkiaGL is causing a compositor hang here, disable temporarily while that gets resolved in bug 908363 -skip-if(Android) load 626602-1.html +skip-if(!haveTestPlugin||http.platform!="X11") load 598862.html +skip-if(Android) load 626602-1.html # bug 908363 load 752340.html +load 843086.xhtml diff --git a/dom/plugins/test/mochitest/hang_test.js b/dom/plugins/test/mochitest/hang_test.js index aabd65f0e3..796093fa3e 100644 --- a/dom/plugins/test/mochitest/hang_test.js +++ b/dom/plugins/test/mochitest/hang_test.js @@ -1,8 +1,8 @@ Components.utils.import("resource://gre/modules/KeyValueParser.jsm"); -const Cc = Components.classes; -const Ci = Components.interfaces; +var Cc = Components.classes; +var Ci = Components.interfaces; var success = false; var observerFired = false; diff --git a/dom/power/PowerManagerService.cpp b/dom/power/PowerManagerService.cpp index eb704ae8d2..41817744e8 100644 --- a/dom/power/PowerManagerService.cpp +++ b/dom/power/PowerManagerService.cpp @@ -10,6 +10,7 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" +#include "jsprf.h" #include "nsIDOMWakeLockListener.h" #include "nsIDOMWindow.h" #include "nsIObserverService.h" @@ -32,6 +33,7 @@ static void LogFunctionAndJSStack(const char* funcname) { "Call to %s. The JS stack is:\n%s\n", funcname, jsstack ? jsstack : ""); + JS_smprintf_free(jsstack); } // bug 839452 #define LOG_FUNCTION_AND_JS_STACK() \ diff --git a/dom/power/test/browser_wakelocks.js b/dom/power/test/browser_wakelocks.js index 341e10f137..3347ee3076 100644 --- a/dom/power/test/browser_wakelocks.js +++ b/dom/power/test/browser_wakelocks.js @@ -6,15 +6,15 @@ waitForExplicitFinish(); -let kUrlSource = "http://mochi.test:8888/"; -let kDataSource = "data:text/html,"; +var kUrlSource = "http://mochi.test:8888/"; +var kDataSource = "data:text/html,"; -let gOldPref; -let gWin, gWin1, gWin2; -let gTab, gTab1, gTab2; -let gLock, gLock1, gLock2; -let gCurStepIndex = -1; -let gSteps = [ +var gOldPref; +var gWin, gWin1, gWin2; +var gTab, gTab1, gTab2; +var gLock, gLock1, gLock2; +var gCurStepIndex = -1; +var gSteps = [ function basicWakeLock() { gTab = gBrowser.addTab(kUrlSource); gWin = gBrowser.getBrowserForTab(gTab).contentWindow; diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index 57a3de81e8..da16969f41 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -525,6 +525,10 @@ Promise::WrapObject(JSContext* aCx, JS::Handle aGivenProto, already_AddRefed Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv) { + if (!aGlobal) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } RefPtr p = new Promise(aGlobal); p->CreateWrapper(nullptr, aRv); if (aRv.Failed()) { @@ -613,6 +617,8 @@ Promise::Then(JSContext* aCx, JS::MutableHandle aRetval, ErrorResult& aRv) { + NS_ASSERT_OWNINGTHREAD(Promise); + // Let's hope this does the right thing with Xrays... Ensure everything is // just in the caller compartment; that ought to do the trick. In theory we // should consider aCalleeGlobal, but in practice our only caller is @@ -694,6 +700,8 @@ void Promise::MaybeResolve(JSContext* aCx, JS::Handle aValue) { + NS_ASSERT_OWNINGTHREAD(Promise); + JS::Rooted p(aCx, PromiseObj()); if (!JS::ResolvePromise(aCx, p, aValue)) { // Now what? There's nothing sane to do here. @@ -705,6 +713,8 @@ void Promise::MaybeReject(JSContext* aCx, JS::Handle aValue) { + NS_ASSERT_OWNINGTHREAD(Promise); + JS::Rooted p(aCx, PromiseObj()); if (!JS::RejectPromise(aCx, p, aValue)) { // Now what? There's nothing sane to do here. @@ -774,6 +784,8 @@ CreateNativeHandlerFunction(JSContext* aCx, JS::Handle aHolder, void Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable) { + NS_ASSERT_OWNINGTHREAD(Promise); + AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(mGlobal))) { // Our API doesn't allow us to return a useful error. Not like this should @@ -851,6 +863,10 @@ already_AddRefed Promise::Create(nsIGlobalObject* aGlobal, ErrorResult& aRv, JS::Handle aDesiredProto) { + if (!aGlobal) { + aRv.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } RefPtr p = new Promise(aGlobal); p->CreateWrapper(aDesiredProto, aRv); if (aRv.Failed()) { @@ -893,6 +909,8 @@ void Promise::MaybeResolve(JSContext* aCx, JS::Handle aValue) { + NS_ASSERT_OWNINGTHREAD(Promise); + MaybeResolveInternal(aCx, aValue); } @@ -900,6 +918,8 @@ void Promise::MaybeReject(JSContext* aCx, JS::Handle aValue) { + NS_ASSERT_OWNINGTHREAD(Promise); + MaybeRejectInternal(aCx, aValue); } @@ -907,12 +927,16 @@ Promise::MaybeReject(JSContext* aCx, void Promise::MaybeReject(const RefPtr& aArg) { + NS_ASSERT_OWNINGTHREAD(Promise); + MaybeSomething(aArg, &Promise::MaybeReject); } void Promise::MaybeRejectWithNull() { + NS_ASSERT_OWNINGTHREAD(Promise); + MaybeSomething(JS::NullHandleValue, &Promise::MaybeReject); } @@ -1626,6 +1650,8 @@ Promise::Then(JSContext* aCx, JS::Handle aCalleeGlobal, AnyCallback* aResolveCallback, AnyCallback* aRejectCallback, JS::MutableHandle aRetval, ErrorResult& aRv) { + NS_ASSERT_OWNINGTHREAD(Promise); + // Implements // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.then @@ -1762,6 +1788,8 @@ Promise::Catch(JSContext* aCx, AnyCallback* aRejectCallback, JS::MutableHandle aRetval, ErrorResult& aRv) { + NS_ASSERT_OWNINGTHREAD(Promise); + // Implements // http://www.ecma-international.org/ecma-262/6.0/#sec-promise.prototype.catch @@ -2413,6 +2441,8 @@ Promise::PromiseSpecies(JSContext* aCx, unsigned aArgc, JS::Value* aVp) void Promise::AppendNativeHandler(PromiseNativeHandler* aRunnable) { + NS_ASSERT_OWNINGTHREAD(Promise); + RefPtr resolveCb = new NativePromiseCallback(aRunnable, Resolved); @@ -2480,6 +2510,8 @@ Promise::AppendCallbacks(PromiseCallback* aResolveCallback, void Promise::MaybeReportRejected() { + NS_ASSERT_OWNINGTHREAD(Promise); + if (mState != Rejected || mHadRejectCallback || mResult.isUndefined()) { return; } @@ -2546,6 +2578,8 @@ void Promise::MaybeResolveInternal(JSContext* aCx, JS::Handle aValue) { + NS_ASSERT_OWNINGTHREAD(Promise); + if (mResolvePending) { return; } @@ -2557,6 +2591,8 @@ void Promise::MaybeRejectInternal(JSContext* aCx, JS::Handle aValue) { + NS_ASSERT_OWNINGTHREAD(Promise); + if (mResolvePending) { return; } @@ -2567,6 +2603,8 @@ Promise::MaybeRejectInternal(JSContext* aCx, void Promise::HandleException(JSContext* aCx) { + NS_ASSERT_OWNINGTHREAD(Promise); + JS::Rooted exn(aCx); if (JS_GetPendingException(aCx, &exn)) { JS_ClearPendingException(aCx); @@ -2578,6 +2616,8 @@ void Promise::ResolveInternal(JSContext* aCx, JS::Handle aValue) { + NS_ASSERT_OWNINGTHREAD(Promise); + CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get(); mResolvePending = true; @@ -2633,6 +2673,8 @@ void Promise::RejectInternal(JSContext* aCx, JS::Handle aValue) { + NS_ASSERT_OWNINGTHREAD(Promise); + mResolvePending = true; MaybeSettle(aValue, Rejected); @@ -2644,21 +2686,30 @@ Promise::Settle(JS::Handle aValue, PromiseState aState) MOZ_ASSERT(mGlobal, "We really should have a global here. Except we sometimes don't " "in the wild for some odd reason"); + NS_ASSERT_OWNINGTHREAD(Promise); + if (!mGlobal || mGlobal->IsDying()) { return; } mSettlementTimestamp = TimeStamp::Now(); - SetResult(aValue); - SetState(aState); - AutoJSAPI jsapi; jsapi.Init(); JSContext* cx = jsapi.cx(); JS::RootedObject wrapper(cx, GetWrapper()); MOZ_ASSERT(wrapper); // We preserved it JSAutoCompartment ac(cx, wrapper); + + JS::Rooted value(cx, aValue); + + if (!JS_WrapValue(cx, &value)) { + JS_ClearPendingException(cx); + value = JS::UndefinedValue(); + } + SetResult(value); + SetState(aState); + JS::dbg::onPromiseSettled(cx, wrapper); if (aState == PromiseState::Rejected && @@ -2698,6 +2749,8 @@ void Promise::MaybeSettle(JS::Handle aValue, PromiseState aState) { + NS_ASSERT_OWNINGTHREAD(Promise); + // Promise.all() or Promise.race() implementations will repeatedly call // Resolve/RejectInternal rather than using the Maybe... forms. Stop SetState // from asserting. @@ -2711,6 +2764,8 @@ Promise::MaybeSettle(JS::Handle aValue, void Promise::TriggerPromiseReactions() { + NS_ASSERT_OWNINGTHREAD(Promise); + CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get(); nsTArray> callbacks; @@ -2730,6 +2785,8 @@ Promise::TriggerPromiseReactions() void Promise::RemoveFeature() { + NS_ASSERT_OWNINGTHREAD(Promise); + if (mFeature) { workers::WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(worker); @@ -2751,6 +2808,8 @@ PromiseReportRejectFeature::Notify(workers::Status aStatus) bool Promise::CaptureStack(JSContext* aCx, JS::Heap& aTarget) { + NS_ASSERT_OWNINGTHREAD(Promise); + JS::Rooted stack(aCx); if (!JS::CaptureCurrentStack(aCx, &stack)) { return false; @@ -2762,6 +2821,8 @@ Promise::CaptureStack(JSContext* aCx, JS::Heap& aTarget) void Promise::GetDependentPromises(nsTArray>& aPromises) { + NS_ASSERT_OWNINGTHREAD(Promise); + // We want to return promises that correspond to then() calls, Promise.all() // calls, and Promise.race() calls. // diff --git a/dom/push/PushComponents.js b/dom/push/PushComponents.js index 876b48cfaf..53be827b76 100644 --- a/dom/push/PushComponents.js +++ b/dom/push/PushComponents.js @@ -24,9 +24,9 @@ XPCOMUtils.defineLazyGetter(this, "PushService", function() { return PushService; }); -// Observer notification topics for system subscriptions. These are duplicated -// and used in `PushNotifier.cpp`. They're exposed on `nsIPushService` instead -// of `nsIPushNotifier` so that JS callers only need to import this service. +// Observer notification topics for push messages and subscription status +// changes. These are duplicated and used in `nsIPushNotifier`. They're exposed +// on `nsIPushService` so that JS callers only need to import this service. const OBSERVER_TOPIC_PUSH = "push-message"; const OBSERVER_TOPIC_SUBSCRIPTION_CHANGE = "push-subscription-change"; const OBSERVER_TOPIC_SUBSCRIPTION_LOST = "push-subscription-lost"; @@ -248,7 +248,7 @@ Object.assign(PushServiceParent.prototype, { // System subscriptions can only be created by chrome callers, and are // exempt from the background message quota and permission checks. They - // also use XPCOM observer notifications instead of service worker events. + // also do not fire service worker events. data.systemRecord = principal.isSystemPrincipal; data.originAttributes = @@ -489,6 +489,15 @@ PushSubscription.prototype = { return this._props.quota; }, + /** + * Indicates whether this subscription was created with the system principal. + * System subscriptions are exempt from the background message quota and + * permission checks. + */ + get isSystemSubscription() { + return !!this._props.systemRecord; + }, + /** * Indicates whether this subscription is subject to the background message * quota. diff --git a/dom/push/PushDB.jsm b/dom/push/PushDB.jsm index a5dae69222..80f0e6a802 100644 --- a/dom/push/PushDB.jsm +++ b/dom/push/PushDB.jsm @@ -404,10 +404,20 @@ this.PushDB.prototype = { aKeyID, newRecord); return; } - aStore.put(newRecord).onsuccess = aEvent => { - console.debug("update: Update successful", aKeyID, newRecord); - aTxn.result = newRecord; - }; + function putRecord() { + let req = aStore.put(newRecord); + req.onsuccess = aEvent => { + console.debug("update: Update successful", aKeyID, newRecord); + aTxn.result = newRecord; + }; + } + if (aKeyID === newRecord.keyID) { + putRecord(); + } else { + // If we changed the primary key, delete the old record to avoid + // unique constraint errors. + aStore.delete(aKeyID).onsuccess = putRecord; + } }; }, resolve, diff --git a/dom/push/PushNotifier.cpp b/dom/push/PushNotifier.cpp index 52204bf0cc..25d6748682 100644 --- a/dom/push/PushNotifier.cpp +++ b/dom/push/PushNotifier.cpp @@ -202,7 +202,7 @@ PushNotifier::NotifyPushWorkers(const nsACString& aScope, // Otherwise, we're in the parent and e10s is enabled. Broadcast the event // to all content processes. - bool ok = false; + bool ok = true; nsTArray contentActors; ContentParent::GetAll(contentActors); for (uint32_t i = 0; i < contentActors.Length(); ++i) { @@ -244,7 +244,7 @@ PushNotifier::NotifySubscriptionChangeWorkers(const nsACString& aScope, } // Parent process, e10s enabled. - bool ok = false; + bool ok = true; nsTArray contentActors; ContentParent::GetAll(contentActors); for (uint32_t i = 0; i < contentActors.Length(); ++i) { @@ -365,8 +365,8 @@ PushNotifier::DoNotifyObservers(nsISupports *aSubject, const char *aTopic, bool PushNotifier::ShouldNotifyWorkers(nsIPrincipal* aPrincipal) { - // System subscriptions use XPCOM observer notifications instead of service - // worker events. The `testing.notifyWorkers` pref disables worker events for + // System subscriptions use observer notifications instead of service worker + // events. The `testing.notifyWorkers` pref disables worker events for // non-system subscriptions. return !nsContentUtils::IsSystemPrincipal(aPrincipal) && Preferences::GetBool("dom.push.testing.notifyWorkers", true); @@ -428,10 +428,7 @@ PushMessage::Json(JSContext* aCx, } ErrorResult error; BodyUtil::ConsumeJson(aCx, aResult, mDecodedText, error); - if (error.Failed()) { - return error.StealNSResult(); - } - return NS_OK; + return error.StealNSResult(); } NS_IMETHODIMP diff --git a/dom/push/PushNotifier.h b/dom/push/PushNotifier.h index 4778f9b073..c4393e8de1 100644 --- a/dom/push/PushNotifier.h +++ b/dom/push/PushNotifier.h @@ -10,37 +10,20 @@ #include "nsCycleCollectionParticipant.h" #include "nsIPrincipal.h" #include "nsString.h" -#include "nsTArray.h" - -#include "mozilla/Maybe.h" - -#define PUSHNOTIFIER_CONTRACTID \ - "@mozilla.org/push/Notifier;1" - -// These constants are duplicated in `PushComponents.js`. -#define OBSERVER_TOPIC_PUSH "push-message" -#define OBSERVER_TOPIC_SUBSCRIPTION_CHANGE "push-subscription-change" -#define OBSERVER_TOPIC_SUBSCRIPTION_LOST "push-subscription-lost" namespace mozilla { namespace dom { -class ContentParent; -class ContentChild; - /** * `PushNotifier` implements the `nsIPushNotifier` interface. This service - * forwards incoming push messages to service workers running in the content - * process, and emits XPCOM observer notifications for system subscriptions. + * broadcasts XPCOM observer notifications for incoming push messages, then + * forwards incoming push messages to service workers. * - * This service exists solely to support `PushService.jsm`. Other callers - * should use `ServiceWorkerManager` directly. + * All scriptable methods on this interface may be called from the parent or + * content process. Observer notifications are broadcasted to both processes. */ class PushNotifier final : public nsIPushNotifier { - friend class ContentParent; - friend class ContentChild; - public: PushNotifier(); @@ -54,19 +37,6 @@ private: nsresult NotifyPush(const nsACString& aScope, nsIPrincipal* aPrincipal, const nsAString& aMessageId, const Maybe>& aData); - nsresult NotifyPushWorkers(const nsACString& aScope, - nsIPrincipal* aPrincipal, - const nsAString& aMessageId, - const Maybe>& aData); - nsresult NotifySubscriptionChangeWorkers(const nsACString& aScope, - nsIPrincipal* aPrincipal); - void NotifyErrorWorkers(const nsACString& aScope, const nsAString& aMessage, - uint32_t aFlags); - nsresult NotifyPushObservers(const nsACString& aScope, - const Maybe>& aData); - nsresult NotifySubscriptionChangeObservers(const nsACString& aScope); - nsresult NotifySubscriptionLostObservers(const nsACString& aScope, - uint16_t aReason); nsresult DoNotifyObservers(nsISupports *aSubject, const char *aTopic, const nsACString& aScope); bool ShouldNotifyWorkers(nsIPrincipal* aPrincipal); @@ -75,8 +45,7 @@ private: /** * `PushMessage` implements the `nsIPushMessage` interface, similar to * the `PushMessageData` WebIDL interface. Instances of this class are - * passed as the subject of `push-message` observer notifications for - * system subscriptions. + * passed as the subject of `push-message` observer notifications. */ class PushMessage final : public nsIPushMessage { @@ -88,10 +57,9 @@ public: nsIPushMessage) NS_DECL_NSIPUSHMESSAGE -protected: +private: virtual ~PushMessage(); -private: nsresult EnsureDecodedText(); nsTArray mData; diff --git a/dom/push/PushRecord.jsm b/dom/push/PushRecord.jsm index e1199c8db0..470f3d1179 100644 --- a/dom/push/PushRecord.jsm +++ b/dom/push/PushRecord.jsm @@ -19,7 +19,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Messaging", XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); - +XPCOMUtils.defineLazyModuleGetter(this, "Task", + "resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); @@ -112,23 +113,19 @@ PushRecord.prototype = { * visited the site, or `-Infinity` if the site is not in the user's history. * The time is expressed in milliseconds since Epoch. */ - getLastVisit() { + getLastVisit: Task.async(function* () { if (!this.quotaApplies() || this.isTabOpen()) { // If the registration isn't subject to quota, or the user already // has the site open, skip expensive database queries. - return Promise.resolve(Date.now()); + return Date.now(); } if (AppConstants.MOZ_ANDROID_HISTORY) { - return Messaging.sendRequestForResult({ + let result = yield Messaging.sendRequestForResult({ type: "History:GetPrePathLastVisitedTimeMilliseconds", prePath: this.uri.prePath, - }).then(result => { - if (result == 0) { - return -Infinity; - } - return result; }); + return result == 0 ? -Infinity : result; } // Places History transition types that can fire a @@ -143,36 +140,35 @@ PushRecord.prototype = { Ci.nsINavHistoryService.TRANSITION_REDIRECT_TEMPORARY ].join(","); - return PlacesUtils.withConnectionWrapper("PushRecord.getLastVisit", db => { - // We're using a custom query instead of `nsINavHistoryQueryOptions` - // because the latter doesn't expose a way to filter by transition type: - // `setTransitions` performs a logical "and," but we want an "or." We - // also avoid an unneeded left join on `moz_favicons`, and an `ORDER BY` - // clause that emits a suboptimal index warning. - return db.executeCached( - `SELECT MAX(p.last_visit_date) - FROM moz_places p - INNER JOIN moz_historyvisits h ON p.id = h.place_id - WHERE ( - p.url >= :urlLowerBound AND p.url <= :urlUpperBound AND - h.visit_type IN (${QUOTA_REFRESH_TRANSITIONS_SQL}) - ) - `, - { - // Restrict the query to all pages for this origin. - urlLowerBound: this.uri.prePath, - urlUpperBound: this.uri.prePath + "\x7f", - } - ); - }).then(rows => { - if (!rows.length) { - return -Infinity; + let db = yield PlacesUtils.promiseDBConnection(); + // We're using a custom query instead of `nsINavHistoryQueryOptions` + // because the latter doesn't expose a way to filter by transition type: + // `setTransitions` performs a logical "and," but we want an "or." We + // also avoid an unneeded left join on `moz_favicons`, and an `ORDER BY` + // clause that emits a suboptimal index warning. + let rows = yield db.executeCached( + `SELECT MAX(visit_date) AS lastVisit + FROM moz_places p + JOIN moz_historyvisits ON p.id = place_id + WHERE rev_host = get_unreversed_host(:host || '.') || '.' + AND url BETWEEN :prePath AND :prePath || X'FFFF' + AND visit_type IN (${QUOTA_REFRESH_TRANSITIONS_SQL}) + `, + { + // Restrict the query to all pages for this origin. + host: this.uri.host, + prePath: this.uri.prePath, } - // Places records times in microseconds. - let lastVisit = rows[0].getResultByIndex(0); - return lastVisit / 1000; - }); - }, + ); + + if (!rows.length) { + return -Infinity; + } + // Places records times in microseconds. + let lastVisit = rows[0].getResultByName("lastVisit"); + + return lastVisit / 1000; + }), isTabOpen() { let windows = Services.wm.getEnumerator("navigator:browser"); @@ -266,6 +262,7 @@ PushRecord.prototype = { authenticationSecret: this.authenticationSecret, appServerKey: this.appServerKey, quota: this.quotaApplies() ? this.quota : -1, + systemRecord: this.systemRecord, }; }, }; diff --git a/dom/push/PushServiceAndroidGCM.jsm b/dom/push/PushServiceAndroidGCM.jsm index c6ef8f3d3b..6b9575164a 100644 --- a/dom/push/PushServiceAndroidGCM.jsm +++ b/dom/push/PushServiceAndroidGCM.jsm @@ -149,11 +149,19 @@ this.PushServiceAndroidGCM = { prefs.observe("debug", this); Services.obs.addObserver(this, "PushServiceAndroidGCM:ReceivedPushMessage", false); - return this._configure(serverURL, !!prefs.get("debug")); + return this._configure(serverURL, !!prefs.get("debug")).then(() => { + Messaging.sendRequestForResult({ + type: "PushServiceAndroidGCM:Initialized" + }); + }); }, uninit: function() { console.debug("uninit()"); + Messaging.sendRequestForResult({ + type: "PushServiceAndroidGCM:Uninitialized" + }); + this._mainPushService = null; Services.obs.removeObserver(this, "PushServiceAndroidGCM:ReceivedPushMessage"); prefs.ignore("debug", this); diff --git a/dom/push/PushServiceWebSocket.jsm b/dom/push/PushServiceWebSocket.jsm index 74a304dc63..2c1815cf46 100644 --- a/dom/push/PushServiceWebSocket.jsm +++ b/dom/push/PushServiceWebSocket.jsm @@ -24,26 +24,19 @@ const { getCryptoParams, } = Cu.import("resource://gre/modules/PushCrypto.jsm"); -XPCOMUtils.defineLazyServiceGetter(this, "gDNSService", - "@mozilla.org/network/dns-service;1", - "nsIDNSService"); - if (AppConstants.MOZ_B2G) { XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService", "@mozilla.org/power/powermanagerservice;1", "nsIPowerManagerService"); } -var threadManager = Cc["@mozilla.org/thread-manager;1"] - .getService(Ci.nsIThreadManager); - const kPUSHWSDB_DB_NAME = "pushapi"; const kPUSHWSDB_DB_VERSION = 5; // Change this if the IndexedDB format changes const kPUSHWSDB_STORE_NAME = "pushapi"; -const kUDP_WAKEUP_WS_STATUS_CODE = 4774; // WebSocket Close status code sent - // by server to signal that it can - // wake client up using UDP. +// WebSocket close code sent by the server to indicate that the client should +// not automatically reconnect. +const kBACKOFF_WS_STATUS_CODE = 4774; // Maps ack statuses, unsubscribe reasons, and delivery error reasons to codes // included in request payloads. @@ -296,47 +289,12 @@ this.PushServiceWebSocket = { * TCP connection after they close a WebSocket. This causes wsOnStop to be * called with error NS_BASE_STREAM_CLOSED. Since the client has to keep the * WebSocket up, it should try to reconnect. But if the server closes the - * WebSocket because it will wake up the client via UDP, then the client - * shouldn't re-establish the connection. If the server says that it will - * wake up the client over UDP, this is set to true in wsOnServerClose. It is + * WebSocket because it wants the client to back off, then the client + * shouldn't re-establish the connection. If the server sends the backoff + * close code, this field will be set to true in wsOnServerClose. It is * checked in wsOnStop. */ - _willBeWokenUpByUDP: false, - - /** - * Holds if the adaptive ping is enabled. This is read on init(). - * If adaptive ping is enabled, a new ping is calculed each time we receive - * a pong message, trying to maximize network resources while minimizing - * cellular signalling storms. - */ - _adaptiveEnabled: false, - - /** - * This saves a flag about if we need to recalculate a new ping, based on: - * 1) the gap between the maximum working ping and the first ping that - * gives an error (timeout) OR - * 2) we have reached the pref of the maximum value we allow for a ping - * (dom.push.adaptive.upperLimit) - */ - _recalculatePing: true, - - /** - * This map holds a (pingInterval, triedTimes) of each pingInterval tried. - * It is used to check if the pingInterval has been tested enough to know that - * is incorrect and is above the limit the network allow us to keep the - * connection open. - */ - _pingIntervalRetryTimes: {}, - - /** - * Holds the lastGoodPingInterval for our current connection. - */ - _lastGoodPingInterval: 0, - - /** - * Maximum ping interval that we can reach. - */ - _upperLimit: 0, + _skipReconnect: false, /** Indicates whether the server supports Web Push-style message delivery. */ _dataEnabled: false, @@ -385,21 +343,7 @@ this.PushServiceWebSocket = { this._makeWebSocket = options.makeWebSocket; } - // Override the default UDP socket factory function. The returned object - // must be null or satisfy the nsIUDPSocket interface. Used by the - // UDP tests. - if (options.makeUDPSocket) { - this._makeUDPSocket = options.makeUDPSocket; - } - - this._networkInfo = options.networkInfo; - if (!this._networkInfo) { - this._networkInfo = PushNetworkInfo; - } - this._requestTimeout = prefs.get("requestTimeout"); - this._adaptiveEnabled = prefs.get('adaptive.enabled'); - this._upperLimit = prefs.get('adaptive.upperLimit'); return Promise.resolve(); }, @@ -413,7 +357,7 @@ this.PushServiceWebSocket = { _shutdownWS: function(shouldCancelPending = true) { console.debug("shutdownWS()"); this._currentState = STATE_SHUT_DOWN; - this._willBeWokenUpByUDP = false; + this._skipReconnect = false; prefs.ignore("userAgentID", this); @@ -442,11 +386,6 @@ this.PushServiceWebSocket = { }, uninit: function() { - if (this._udpServer) { - this._udpServer.close(); - this._udpServer = null; - } - // All pending requests (ideally none) are dropped at this point. We // shouldn't have any applications performing registration/unregistration // or receiving notifications. @@ -465,9 +404,7 @@ this.PushServiceWebSocket = { }, /** - * How retries work: The goal is to ensure websocket is always up on - * networks not supporting UDP. So the websocket should only be shutdown if - * onServerClose indicates UDP wakeup. If WS is closed due to socket error, + * How retries work: If the WS is closed due to a socket error, * _startBackoffTimer() is called. The retry timer is started and when * it times out, beginWSSetup() is called again. * @@ -480,8 +417,6 @@ this.PushServiceWebSocket = { */ _startBackoffTimer() { console.debug("startBackoffTimer()"); - //Calculate new ping interval - this._calculateAdaptivePing(true /* wsWentDown */); // Calculate new timeout, but cap it to pingInterval. let retryTimeout = prefs.get("retryBaseInterval") * @@ -532,156 +467,6 @@ this.PushServiceWebSocket = { Ci.nsITimer.TYPE_ONE_SHOT); }, - /** - * We need to calculate a new ping based on: - * 1) Latest good ping - * 2) A safe gap between 1) and the calculated new ping (which is - * by default, 1 minute) - * - * This is for 3G networks, whose connections keepalives differ broadly, - * for example: - * 1) Movistar Spain: 29 minutes - * 2) VIVO Brazil: 5 minutes - * 3) Movistar Colombia: XXX minutes - * - * So a fixed ping is not good for us for two reasons: - * 1) We might lose the connection, so we need to reconnect again (wasting - * resources) - * 2) We use a lot of network signaling just for pinging. - * - * This algorithm tries to search the best value between a disconnection and a - * valid ping, to ensure better battery life and network resources usage. - * - * The value is saved in dom.push.pingInterval - * @param wsWentDown [Boolean] if the WebSocket was closed or it is still - * alive - * - */ - _calculateAdaptivePing: function(wsWentDown) { - console.debug("_calculateAdaptivePing()"); - if (!this._adaptiveEnabled) { - console.debug("calculateAdaptivePing: Adaptive ping is disabled"); - return; - } - - if (this._retryFailCount > 0) { - console.warn("calculateAdaptivePing: Push has failed to connect to the", - "Push Server", this._retryFailCount, "times. Do not calculate a new", - "pingInterval now"); - return; - } - - if (!this._recalculatePing && !wsWentDown) { - console.debug("calculateAdaptivePing: We do not need to recalculate the", - "ping now, based on previous data"); - return; - } - - // Save actual state of the network - let ns = this._networkInfo.getNetworkInformation(); - - if (ns.ip) { - // mobile - console.debug("calculateAdaptivePing: mobile"); - let oldNetwork = prefs.get('adaptive.mobile'); - let newNetwork = 'mobile-' + ns.mcc + '-' + ns.mnc; - - // Mobile networks differ, reset all intervals and pings - if (oldNetwork !== newNetwork) { - // Network differ, reset all values - console.debug("calculateAdaptivePing: Mobile networks differ. Old", - "network is", oldNetwork, "and new is", newNetwork); - prefs.set('adaptive.mobile', newNetwork); - //We reset the upper bound member - this._recalculatePing = true; - this._pingIntervalRetryTimes = {}; - - // Put default values - let defaultPing = prefs.get('pingInterval.default'); - prefs.set('pingInterval', defaultPing); - this._lastGoodPingInterval = defaultPing; - - } else { - // Mobile network is the same, let's just update things - prefs.set('pingInterval', prefs.get('pingInterval.mobile')); - this._lastGoodPingInterval = prefs.get('adaptive.lastGoodPingInterval.mobile'); - } - - } else { - // wifi - console.debug("calculateAdaptivePing: wifi"); - prefs.set('pingInterval', prefs.get('pingInterval.wifi')); - this._lastGoodPingInterval = prefs.get('adaptive.lastGoodPingInterval.wifi'); - } - - let nextPingInterval; - let lastTriedPingInterval = prefs.get('pingInterval'); - - if (wsWentDown) { - console.debug("calculateAdaptivePing: The WebSocket was disconnected.", - "Calculating next ping"); - - // If we have not tried this pingInterval yet, initialize - this._pingIntervalRetryTimes[lastTriedPingInterval] = - (this._pingIntervalRetryTimes[lastTriedPingInterval] || 0) + 1; - - // Try the pingInterval at least 3 times, just to be sure that the - // calculated interval is not valid. - if (this._pingIntervalRetryTimes[lastTriedPingInterval] < 2) { - console.debug("calculateAdaptivePing: pingInterval=", - lastTriedPingInterval, "tried only", - this._pingIntervalRetryTimes[lastTriedPingInterval], "times"); - return; - } - - // Latest ping was invalid, we need to lower the limit to limit / 2 - nextPingInterval = Math.floor(lastTriedPingInterval / 2); - - // If the new ping interval is close to the last good one, we are near - // optimum, so stop calculating. - if (nextPingInterval - this._lastGoodPingInterval < - prefs.get('adaptive.gap')) { - console.debug("calculateAdaptivePing: We have reached the gap, we", - "have finished the calculation. nextPingInterval=", nextPingInterval, - "lastGoodPing=", this._lastGoodPingInterval); - nextPingInterval = this._lastGoodPingInterval; - this._recalculatePing = false; - } else { - console.debug("calculateAdaptivePing: We need to calculate next time"); - this._recalculatePing = true; - } - - } else { - console.debug("calculateAdaptivePing: The WebSocket is still up"); - this._lastGoodPingInterval = lastTriedPingInterval; - nextPingInterval = Math.floor(lastTriedPingInterval * 1.5); - } - - // Check if we have reached the upper limit - if (this._upperLimit < nextPingInterval) { - console.debug("calculateAdaptivePing: Next ping will be bigger than the", - "configured upper limit, capping interval"); - this._recalculatePing = false; - this._lastGoodPingInterval = lastTriedPingInterval; - nextPingInterval = lastTriedPingInterval; - } - - console.debug("calculateAdaptivePing: Setting the pingInterval to", - nextPingInterval); - prefs.set('pingInterval', nextPingInterval); - - //Save values for our current network - if (ns.ip) { - prefs.set('pingInterval.mobile', nextPingInterval); - prefs.set('adaptive.lastGoodPingInterval.mobile', - this._lastGoodPingInterval); - } else { - prefs.set('pingInterval.wifi', nextPingInterval); - prefs.set('adaptive.lastGoodPingInterval.wifi', - this._lastGoodPingInterval); - } - }, - _makeWebSocket: function(uri) { if (!prefs.get("connection.enabled")) { console.warn("makeWebSocket: connection.enabled is not set to true.", @@ -1197,27 +982,8 @@ this.PushServiceWebSocket = { data.uaid = this._UAID; } - this._networkInfo.getNetworkState((networkState) => { - if (networkState.ip) { - // Opening an available UDP port. - this._listenForUDPWakeup(); - - // Host-port is apparently a thing. - data.wakeup_hostport = { - ip: networkState.ip, - port: this._udpServer && this._udpServer.port - }; - - data.mobilenetwork = { - mcc: networkState.mcc, - mnc: networkState.mnc, - netid: networkState.netid - }; - } - - this._wsSendMessage(data); - this._currentState = STATE_WAITING_FOR_HELLO; - }); + this._wsSendMessage(data); + this._currentState = STATE_WAITING_FOR_HELLO; }, /** @@ -1231,9 +997,8 @@ this.PushServiceWebSocket = { console.debug("wsOnStop()"); this._releaseWakeLock(); - if (statusCode != Cr.NS_OK && - !(statusCode == Cr.NS_BASE_STREAM_CLOSED && this._willBeWokenUpByUDP)) { - console.debug("wsOnStop: Socket error", statusCode); + if (statusCode != Cr.NS_OK && !this._skipReconnect) { + console.debug("wsOnStop: Reconnecting after socket error", statusCode); this._reconnect(); return; } @@ -1258,7 +1023,6 @@ this.PushServiceWebSocket = { // If we receive a message, we know the connection succeeded. Reset the // connection attempt and ping interval counters. this._retryFailCount = 0; - this._pingIntervalRetryTimes = {}; let doNotHandle = false; if ((message === '{}') || @@ -1266,7 +1030,6 @@ this.PushServiceWebSocket = { (reply.messageType === "ping") || (typeof reply.messageType != "string")) { console.debug("wsOnMessageAvailable: Pong received"); - this._calculateAdaptivePing(false); doNotHandle = true; } @@ -1311,17 +1074,16 @@ this.PushServiceWebSocket = { * function), which calls reconnect and re-establishes the WebSocket * connection. * - * If the server said it'll use UDP for wakeup, we set _willBeWokenUpByUDP - * and stop reconnecting in _wsOnStop(). + * If the server requested that we back off, we won't reconnect until the + * next network state change event, or until we need to send a new register + * request. */ _wsOnServerClose: function(context, aStatusCode, aReason) { console.debug("wsOnServerClose()", aStatusCode, aReason); - // Switch over to UDP. - if (aStatusCode == kUDP_WAKEUP_WS_STATUS_CODE) { - console.debug("wsOnServerClose: Server closed with promise to wake up"); - this._willBeWokenUpByUDP = true; - // TODO: there should be no pending requests + if (aStatusCode == kBACKOFF_WS_STATUS_CODE) { + console.debug("wsOnServerClose: Skipping automatic reconnect"); + this._skipReconnect = true; } }, @@ -1334,187 +1096,6 @@ this.PushServiceWebSocket = { } this._registerRequests.clear(); }, - - _makeUDPSocket: function() { - return Cc["@mozilla.org/network/udp-socket;1"] - .createInstance(Ci.nsIUDPSocket); - }, - - /** - * This method should be called only if the device is on a mobile network! - */ - _listenForUDPWakeup: function() { - console.debug("listenForUDPWakeup()"); - - if (this._udpServer) { - console.warn("listenForUDPWakeup: UDP Server already running"); - return; - } - - if (!prefs.get("udp.wakeupEnabled")) { - console.debug("listenForUDPWakeup: UDP support disabled"); - return; - } - - let socket = this._makeUDPSocket(); - if (!socket) { - return; - } - this._udpServer = socket.QueryInterface(Ci.nsIUDPSocket); - this._udpServer.init(-1, false, Services.scriptSecurityManager.getSystemPrincipal()); - this._udpServer.asyncListen(this); - console.debug("listenForUDPWakeup: Listening on", this._udpServer.port); - - return this._udpServer.port; - }, - - /** - * Called by UDP Server Socket. As soon as a ping is recieved via UDP, - * reconnect the WebSocket and get the actual data. - */ - onPacketReceived: function(aServ, aMessage) { - console.debug("onPacketReceived: Recv UDP datagram on port", - this._udpServer.port); - this._beginWSSetup(); - }, - - /** - * Called by UDP Server Socket if the socket was closed for some reason. - * - * If this happens, we reconnect the WebSocket to not miss out on - * notifications. - */ - onStopListening: function(aServ, aStatus) { - console.debug("onStopListening: UDP Server socket was shutdown. Status", - aStatus); - this._udpServer = undefined; - this._beginWSSetup(); - }, -}; - -var PushNetworkInfo = { - /** - * Returns information about MCC-MNC and the IP of the current connection. - */ - getNetworkInformation: function() { - console.debug("PushNetworkInfo: getNetworkInformation()"); - - try { - if (!prefs.get("udp.wakeupEnabled")) { - console.debug("getNetworkInformation: UDP support disabled, we do not", - "send any carrier info"); - throw new Error("UDP disabled"); - } - - let nm = Cc["@mozilla.org/network/manager;1"] - .getService(Ci.nsINetworkManager); - if (nm.activeNetworkInfo && - nm.activeNetworkInfo.type == Ci.nsINetworkInfo.NETWORK_TYPE_MOBILE) { - let iccService = Cc["@mozilla.org/icc/iccservice;1"] - .getService(Ci.nsIIccService); - // TODO: Bug 927721 - PushService for multi-sim - // In Multi-sim, there is more than one client in iccService. Each - // client represents a icc handle. To maintain backward compatibility - // with single sim, we always use client 0 for now. Adding support - // for multiple sim will be addressed in bug 927721, if needed. - let clientId = 0; - let icc = iccService.getIccByServiceId(clientId); - let iccInfo = icc && icc.iccInfo; - if (iccInfo) { - console.debug("getNetworkInformation: Running on mobile data"); - - let ips = {}; - let prefixLengths = {}; - nm.activeNetworkInfo.getAddresses(ips, prefixLengths); - - return { - mcc: iccInfo.mcc, - mnc: iccInfo.mnc, - ip: ips.value[0] - }; - } - } - } catch (e) { - console.error("getNetworkInformation: Error recovering mobile network", - "information", e); - } - - console.debug("getNetworkInformation: Running on wifi"); - return { - mcc: 0, - mnc: 0, - ip: undefined - }; - }, - - /** - * Get mobile network information to decide if the client is capable of being - * woken up by UDP (which currently just means having an mcc and mnc along - * with an IP, and optionally a netid). - */ - getNetworkState: function(callback) { - console.debug("PushNetworkInfo: getNetworkState()"); - - if (typeof callback !== 'function') { - throw new Error("No callback method. Aborting push agent !"); - } - - var networkInfo = this.getNetworkInformation(); - - if (networkInfo.ip) { - this._getMobileNetworkId(networkInfo, function(netid) { - console.debug("getNetworkState: Recovered netID", netid); - callback({ - mcc: networkInfo.mcc, - mnc: networkInfo.mnc, - ip: networkInfo.ip, - netid: netid - }); - }); - } else { - callback(networkInfo); - } - }, - - /* - * Get the mobile network ID (netid) - * - * @param networkInfo - * Network information object { mcc, mnc, ip, port } - * @param callback - * Callback function to invoke with the netid or null if not found - */ - _getMobileNetworkId: function(networkInfo, callback) { - console.debug("PushNetworkInfo: getMobileNetworkId()"); - if (typeof callback !== 'function') { - return; - } - - function queryDNSForDomain(domain) { - console.debug("queryDNSForDomain: Querying DNS for", domain); - let netIDDNSListener = { - onLookupComplete: function(aRequest, aRecord, aStatus) { - if (aRecord) { - let netid = aRecord.getNextAddrAsString(); - console.debug("queryDNSForDomain: NetID found", netid); - callback(netid); - } else { - console.debug("queryDNSForDomain: NetID not found"); - callback(null); - } - } - }; - gDNSService.asyncResolve(domain, 0, netIDDNSListener, - threadManager.currentThread); - return []; - } - - console.debug("getMobileNetworkId: Getting mobile network ID"); - - let netidAddress = "wakeup.mnc" + ("00" + networkInfo.mnc).slice(-3) + - ".mcc" + ("00" + networkInfo.mcc).slice(-3) + ".3gppnetwork.org"; - queryDNSForDomain(netidAddress, callback); - } }; function PushRecordWebSocket(record) { diff --git a/dom/push/test/mockpushserviceparent.js b/dom/push/test/mockpushserviceparent.js index 8bd73d7c22..78cf246ee8 100644 --- a/dom/push/test/mockpushserviceparent.js +++ b/dom/push/test/mockpushserviceparent.js @@ -61,22 +61,6 @@ MockWebSocketParent.prototype = { }, }; -function MockNetworkInfo() {} - -MockNetworkInfo.prototype = { - getNetworkInformation() { - return {mcc: '', mnc: '', ip: ''}; - }, - - getNetworkState(callback) { - callback({mcc: '', mnc: '', ip: '', netid: ''}); - }, - - getNetworkStateChangeEventName() { - return 'network:offline-status-changed'; - } -}; - var pushService = Cc["@mozilla.org/push/Service;1"]. getService(Ci.nsIPushService). wrappedJSObject; @@ -87,7 +71,6 @@ var serverMsgs = []; addMessageListener("socket-setup", function () { pushService.replaceServiceBackend({ serverURI: "wss://push.example.org/", - networkInfo: new MockNetworkInfo(), makeWebSocket(uri) { mockSocket = new MockWebSocketParent(uri); while (serverMsgs.length > 0) { diff --git a/dom/push/test/xpcshell/head.js b/dom/push/test/xpcshell/head.js index 2ddbc26c33..89dd4ff9a5 100644 --- a/dom/push/test/xpcshell/head.js +++ b/dom/push/test/xpcshell/head.js @@ -13,6 +13,8 @@ Cu.import('resource://gre/modules/Promise.jsm'); Cu.import('resource://gre/modules/Preferences.jsm'); Cu.import('resource://gre/modules/PlacesUtils.jsm'); Cu.import('resource://gre/modules/ObjectUtils.jsm'); +XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils", + "resource://testing-common/PlacesTestUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, 'PlacesTestUtils', 'resource://testing-common/PlacesTestUtils.jsm'); @@ -64,25 +66,6 @@ function after(times, func) { }; } -/** - * Updates the places database. - * - * @param {mozIPlaceInfo} place A place record to insert. - * @returns {Promise} A promise that fulfills when the database is updated. - */ -function addVisit(place) { - return new Promise((resolve, reject) => { - if (typeof place.uri == 'string') { - place.uri = Services.io.newURI(place.uri, null, null); - } - PlacesUtils.asyncHistory.updatePlaces(place, { - handleCompletion: resolve, - handleError: reject, - handleResult() {}, - }); - }); -} - /** * Defers one or more callbacks until the next turn of the event loop. Multiple * callbacks are executed in order. @@ -159,24 +142,11 @@ function setPrefs(prefs = {}) { 'connection.enabled': true, userAgentID: '', enabled: true, - // Disable adaptive pings and UDP wake-up by default; these are - // tested separately. - 'adaptive.enabled': false, - 'udp.wakeupEnabled': false, - // Defaults taken from /b2g/app/b2g.js. + // Defaults taken from /modules/libpref/init/all.js. requestTimeout: 10000, retryBaseInterval: 5000, pingInterval: 30 * 60 * 1000, - 'pingInterval.default': 3 * 60 * 1000, - 'pingInterval.mobile': 3 * 60 * 1000, - 'pingInterval.wifi': 3 * 60 * 1000, - 'adaptive.lastGoodPingInterval': 3 * 60 * 1000, - 'adaptive.lastGoodPingInterval.mobile': 3 * 60 * 1000, - 'adaptive.lastGoodPingInterval.wifi': 3 * 60 * 1000, - 'adaptive.gap': 60000, - 'adaptive.upperLimit': 29 * 60 * 1000, // Misc. defaults. - 'adaptive.mobile': '', 'http2.maxRetries': 2, 'http2.retryInterval': 500, 'http2.reset_retry_count_after_ms': 60000, @@ -326,8 +296,7 @@ MockWebSocket.prototype = { /** * Closes the server end of the connection, calling onServerClose() - * followed by onStop(). Used to test abrupt connection termination - * and UDP wake-up. + * followed by onStop(). Used to test abrupt connection termination. * * @param {Number} [statusCode] The WebSocket connection close code. * @param {String} [reason] The connection close reason. @@ -347,58 +316,6 @@ MockWebSocket.prototype = { }, }; -/** - * Creates an object that exposes the same interface as NetworkInfo, used - * to simulate network status changes on Desktop. All methods returns empty - * carrier data. - */ -function MockDesktopNetworkInfo() {} - -MockDesktopNetworkInfo.prototype = { - getNetworkInformation() { - return {mcc: '', mnc: '', ip: ''}; - }, - - getNetworkState(callback) { - callback({mcc: '', mnc: '', ip: '', netid: ''}); - }, - - getNetworkStateChangeEventName() { - return 'network:offline-status-changed'; - } -}; - -/** - * Creates an object that exposes the same interface as NetworkInfo, used - * to simulate network status changes on B2G. - * - * @param {String} [info.mcc] The mobile country code. - * @param {String} [info.mnc] The mobile network code. - * @param {String} [info.ip] The carrier IP address. - * @param {String} [info.netid] The resolved network ID for UDP wake-up. - */ -function MockMobileNetworkInfo(info = {}) { - this._info = info; -} - -MockMobileNetworkInfo.prototype = { - _info: null, - - getNetworkInformation() { - let {mcc, mnc, ip} = this._info; - return {mcc, mnc, ip}; - }, - - getNetworkState(callback) { - let {mcc, mnc, ip, netid} = this._info; - callback({mcc, mnc, ip, netid}); - }, - - getNetworkStateChangeEventName() { - return 'network-active-changed'; - } -}; - var setUpServiceInParent = Task.async(function* (service, db) { if (!isParent) { return; @@ -442,7 +359,6 @@ var setUpServiceInParent = Task.async(function* (service, db) { service.init({ serverURI: 'wss://push.example.org/', - networkInfo: new MockDesktopNetworkInfo(), db: makeStub(db, { put(prev, record) { if (record.scope == 'https://example.com/sub/fail') { diff --git a/dom/push/test/xpcshell/test_clearAll_successful.js b/dom/push/test/xpcshell/test_clearAll_successful.js index 5bdf34df89..f4eab2728d 100644 --- a/dom/push/test/xpcshell/test_clearAll_successful.js +++ b/dom/push/test/xpcshell/test_clearAll_successful.js @@ -27,7 +27,6 @@ add_task(function* test_unregister_success() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_clear_origin_data.js b/dom/push/test/xpcshell/test_clear_origin_data.js index 28dd14f757..ca2a891cfb 100644 --- a/dom/push/test/xpcshell/test_clear_origin_data.js +++ b/dom/push/test/xpcshell/test_clear_origin_data.js @@ -84,7 +84,6 @@ add_task(function* test_webapps_cleardata() { PushService.init({ serverURI: "wss://push.example.org", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_drop_expired.js b/dom/push/test/xpcshell/test_drop_expired.js index d2a6ff06fd..248b78cef2 100644 --- a/dom/push/test/xpcshell/test_drop_expired.js +++ b/dom/push/test/xpcshell/test_drop_expired.js @@ -12,13 +12,11 @@ var quotaURI; var permURI; function visitURI(uri, timestamp) { - return addVisit({ + return PlacesTestUtils.addVisits({ uri: uri, title: uri.spec, - visits: [{ - visitDate: timestamp * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_LINK, - }], + visitDate: timestamp * 1000, + transition: Ci.nsINavHistoryService.TRANSITION_LINK }); } @@ -107,7 +105,6 @@ add_task(function* setUp() { PushService.init({ serverURI: 'wss://push.example.org/', - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_notification_ack.js b/dom/push/test/xpcshell/test_notification_ack.js index b005922089..19c5a158ad 100644 --- a/dom/push/test/xpcshell/test_notification_ack.js +++ b/dom/push/test/xpcshell/test_notification_ack.js @@ -5,7 +5,7 @@ const {PushDB, PushService, PushServiceWebSocket} = serviceExports; -let userAgentID = '5ab1d1df-7a3d-4024-a469-b9e1bb399fad'; +var userAgentID = '5ab1d1df-7a3d-4024-a469-b9e1bb399fad'; function run_test() { do_get_profile(); @@ -54,7 +54,6 @@ add_task(function* test_notification_ack() { let ackPromise = new Promise(resolve => ackDone = resolve); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_notification_data.js b/dom/push/test/xpcshell/test_notification_data.js index 0982b78cde..c6e3059ff0 100644 --- a/dom/push/test/xpcshell/test_notification_data.js +++ b/dom/push/test/xpcshell/test_notification_data.js @@ -93,7 +93,6 @@ add_task(function* test_notification_ack_data_setup() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_notification_duplicate.js b/dom/push/test/xpcshell/test_notification_duplicate.js index 83178d55b0..ad372f4dde 100644 --- a/dom/push/test/xpcshell/test_notification_duplicate.js +++ b/dom/push/test/xpcshell/test_notification_duplicate.js @@ -47,7 +47,6 @@ add_task(function* test_notification_duplicate() { let ackPromise = new Promise(resolve => ackDone = after(2, resolve)); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_notification_error.js b/dom/push/test/xpcshell/test_notification_error.js index 4a82c41527..74631f4f81 100644 --- a/dom/push/test/xpcshell/test_notification_error.js +++ b/dom/push/test/xpcshell/test_notification_error.js @@ -57,7 +57,6 @@ add_task(function* test_notification_error() { let ackPromise = new Promise(resolve => ackDone = after(records.length, resolve)); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db: makeStub(db, { getByKeyID(prev, channelID) { if (channelID == '3c3930ba-44de-40dc-a7ca-8a133ec1a866') { diff --git a/dom/push/test/xpcshell/test_notification_incomplete.js b/dom/push/test/xpcshell/test_notification_incomplete.js index 3b73d0d8d9..ed2cec9863 100644 --- a/dom/push/test/xpcshell/test_notification_incomplete.js +++ b/dom/push/test/xpcshell/test_notification_incomplete.js @@ -67,7 +67,6 @@ add_task(function* test_notification_incomplete() { }; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_notification_version_string.js b/dom/push/test/xpcshell/test_notification_version_string.js index fa02d6d006..81c223a659 100644 --- a/dom/push/test/xpcshell/test_notification_version_string.js +++ b/dom/push/test/xpcshell/test_notification_version_string.js @@ -34,7 +34,6 @@ add_task(function* test_notification_version_string() { let ackPromise = new Promise(resolve => ackDone = resolve); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_observer_data.js b/dom/push/test/xpcshell/test_observer_data.js new file mode 100644 index 0000000000..58dad738ee --- /dev/null +++ b/dom/push/test/xpcshell/test_observer_data.js @@ -0,0 +1,40 @@ +'use strict'; + +var pushNotifier = Cc['@mozilla.org/push/Notifier;1'] + .getService(Ci.nsIPushNotifier); +var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal(); + +function run_test() { + run_next_test(); +} + +add_task(function* test_notifyWithData() { + let textData = '{"hello":"world"}'; + let data = new TextEncoder('utf-8').encode(textData); + + let notifyPromise = + promiseObserverNotification(PushServiceComponent.pushTopic); + pushNotifier.notifyPushWithData('chrome://notify-test', systemPrincipal, + '' /* messageId */, data.length, data); + + let message = (yield notifyPromise).subject.QueryInterface(Ci.nsIPushMessage); + deepEqual(message.json(), { + hello: 'world', + }, 'Should extract JSON values'); + deepEqual(message.binary(), Array.from(data), + 'Should extract raw binary data'); + equal(message.text(), textData, 'Should extract text data'); +}); + +add_task(function* test_empty_notifyWithData() { + let notifyPromise = + promiseObserverNotification(PushServiceComponent.pushTopic); + pushNotifier.notifyPushWithData('chrome://notify-test', systemPrincipal, + '' /* messageId */, 0, null); + + let message = (yield notifyPromise).subject.QueryInterface(Ci.nsIPushMessage); + throws(_ => message.json(), + 'Should throw an error when parsing an empty string as JSON'); + strictEqual(message.text(), '', 'Should return an empty string'); + deepEqual(message.binary(), [], 'Should return an empty array'); +}); diff --git a/dom/push/test/xpcshell/test_permissions.js b/dom/push/test/xpcshell/test_permissions.js index 24ddd80bd3..cbea1ea187 100644 --- a/dom/push/test/xpcshell/test_permissions.js +++ b/dom/push/test/xpcshell/test_permissions.js @@ -101,7 +101,6 @@ add_task(function* setUp() { let handshakePromise = new Promise(resolve => handshakeDone = resolve); PushService.init({ serverURI: 'wss://push.example.org/', - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_quota_exceeded.js b/dom/push/test/xpcshell/test_quota_exceeded.js index 3342f7ae58..1982fe04ce 100644 --- a/dom/push/test/xpcshell/test_quota_exceeded.js +++ b/dom/push/test/xpcshell/test_quota_exceeded.js @@ -45,37 +45,34 @@ add_task(function* test_expiration_origin_threshold() { // The notification threshold is per-origin, even with multiple service // workers for different scopes. - yield addVisit({ - uri: 'https://example.com/login', - title: 'Sign in to see your auctions', - visits: [{ + yield PlacesTestUtils.addVisits([ + { + uri: 'https://example.com/login', + title: 'Sign in to see your auctions', visitDate: (Date.now() - 7 * 24 * 60 * 60 * 1000) * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_LINK, - }], - }); - - // We'll always use your most recent visit to an origin. - yield addVisit({ - uri: 'https://example.com/auctions', - title: 'Your auctions', - visits: [{ + transition: Ci.nsINavHistoryService.TRANSITION_LINK + }, + // We'll always use your most recent visit to an origin. + { + uri: 'https://example.com/auctions', + title: 'Your auctions', visitDate: (Date.now() - 2 * 24 * 60 * 60 * 1000) * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_LINK, - }], - }); - - // ...But we won't count downloads or embeds. - yield addVisit({ - uri: 'https://example.com/invoices/invoice.pdf', - title: 'Invoice #123', - visits: [{ + transition: Ci.nsINavHistoryService.TRANSITION_LINK + }, + // ...But we won't count downloads or embeds. + { + uri: 'https://example.com/invoices/invoice.pdf', + title: 'Invoice #123', visitDate: (Date.now() - 1 * 24 * 60 * 60 * 1000) * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_EMBED, - }, { + transition: Ci.nsINavHistoryService.TRANSITION_EMBED + }, + { + uri: 'https://example.com/invoices/invoice.pdf', + title: 'Invoice #123', visitDate: Date.now() * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD, - }], - }); + transition: Ci.nsINavHistoryService.TRANSITION_DOWNLOAD + } + ]); // We expect to receive 6 notifications: 5 on the `auctions` channel, // and 1 on the `deals` channel. They're from the same origin, but @@ -92,7 +89,6 @@ add_task(function* test_expiration_origin_threshold() { PushService.init({ serverURI: 'wss://push.example.org/', - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_quota_observer.js b/dom/push/test/xpcshell/test_quota_observer.js index 7f49dc2bf5..1842cbf3bf 100644 --- a/dom/push/test/xpcshell/test_quota_observer.js +++ b/dom/push/test/xpcshell/test_quota_observer.js @@ -57,13 +57,11 @@ add_task(function* test_expiration_history_observer() { quota: 0, }); - yield addVisit({ + yield PlacesTestUtils.addVisits({ uri: 'https://example.com/infrequent', title: 'Infrequently-visited page', - visits: [{ - visitDate: (Date.now() - 14 * 24 * 60 * 60 * 1000) * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_LINK, - }], + visitDate: (Date.now() - 14 * 24 * 60 * 60 * 1000) * 1000, + transition: Ci.nsINavHistoryService.TRANSITION_LINK }); let unregisterDone; @@ -73,7 +71,6 @@ add_task(function* test_expiration_history_observer() { PushService.init({ serverURI: 'wss://push.example.org/', - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { @@ -126,13 +123,11 @@ add_task(function* test_expiration_history_observer() { }); // Now visit the site... - yield addVisit({ + yield PlacesTestUtils.addVisits({ uri: 'https://example.com/another-page', title: 'Infrequently-visited page', - visits: [{ - visitDate: Date.now() * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_LINK, - }], + visitDate: Date.now() * 1000, + transition: Ci.nsINavHistoryService.TRANSITION_LINK }); Services.obs.notifyObservers(null, 'idle-daily', ''); diff --git a/dom/push/test/xpcshell/test_quota_with_notification.js b/dom/push/test/xpcshell/test_quota_with_notification.js index c62e554bec..d5b16cdef4 100644 --- a/dom/push/test/xpcshell/test_quota_with_notification.js +++ b/dom/push/test/xpcshell/test_quota_with_notification.js @@ -43,13 +43,11 @@ add_task(function* test_expiration_origin_threshold() { }); // A visit one day ago should provide a quota of 8 messages. - yield addVisit({ + yield PlacesTestUtils.addVisits({ uri: 'https://example.com/login', title: 'Sign in to see your auctions', - visits: [{ - visitDate: (Date.now() - 1 * 24 * 60 * 60 * 1000) * 1000, - transitionType: Ci.nsINavHistoryService.TRANSITION_LINK, - }], + visitDate: (Date.now() - 1 * 24 * 60 * 60 * 1000) * 1000, + transition: Ci.nsINavHistoryService.TRANSITION_LINK }); let numMessages = 10; @@ -72,7 +70,6 @@ add_task(function* test_expiration_origin_threshold() { PushService.init({ serverURI: 'wss://push.example.org/', - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_reconnect_retry.js b/dom/push/test/xpcshell/test_reconnect_retry.js index 9141ff47c0..71ce2c0f2e 100644 --- a/dom/push/test/xpcshell/test_reconnect_retry.js +++ b/dom/push/test/xpcshell/test_reconnect_retry.js @@ -22,7 +22,6 @@ add_task(function* test_reconnect_retry() { let channelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_register_case.js b/dom/push/test/xpcshell/test_register_case.js index af915d8d88..98670c742d 100644 --- a/dom/push/test/xpcshell/test_register_case.js +++ b/dom/push/test/xpcshell/test_register_case.js @@ -19,7 +19,6 @@ add_task(function* test_register_case() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_register_flush.js b/dom/push/test/xpcshell/test_register_flush.js index 8c0db44941..49d2fe6745 100644 --- a/dom/push/test/xpcshell/test_register_flush.js +++ b/dom/push/test/xpcshell/test_register_flush.js @@ -38,7 +38,6 @@ add_task(function* test_register_flush() { let ackPromise = new Promise(resolve => ackDone = after(2, resolve)); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_register_invalid_channel.js b/dom/push/test/xpcshell/test_register_invalid_channel.js index 8d29d5fa9c..48de1f2b9e 100644 --- a/dom/push/test/xpcshell/test_register_invalid_channel.js +++ b/dom/push/test/xpcshell/test_register_invalid_channel.js @@ -21,7 +21,6 @@ add_task(function* test_register_invalid_channel() { PushServiceWebSocket._generateID = () => channelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_register_invalid_endpoint.js b/dom/push/test/xpcshell/test_register_invalid_endpoint.js index ffafc64cda..35e235c4e4 100644 --- a/dom/push/test/xpcshell/test_register_invalid_endpoint.js +++ b/dom/push/test/xpcshell/test_register_invalid_endpoint.js @@ -21,7 +21,6 @@ add_task(function* test_register_invalid_endpoint() { PushServiceWebSocket._generateID = () => channelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_register_invalid_json.js b/dom/push/test/xpcshell/test_register_invalid_json.js index 9bf6ca2159..19f567101e 100644 --- a/dom/push/test/xpcshell/test_register_invalid_json.js +++ b/dom/push/test/xpcshell/test_register_invalid_json.js @@ -25,7 +25,6 @@ add_task(function* test_register_invalid_json() { PushServiceWebSocket._generateID = () => channelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { diff --git a/dom/push/test/xpcshell/test_register_no_id.js b/dom/push/test/xpcshell/test_register_no_id.js index 3224528acf..0a96001a6d 100644 --- a/dom/push/test/xpcshell/test_register_no_id.js +++ b/dom/push/test/xpcshell/test_register_no_id.js @@ -5,8 +5,8 @@ const {PushDB, PushService, PushServiceWebSocket} = serviceExports; -let userAgentID = '9a2f9efe-2ebb-4bcb-a5d9-9e2b73d30afe'; -let channelID = '264c2ba0-f6db-4e84-acdb-bd225b62d9e3'; +var userAgentID = '9a2f9efe-2ebb-4bcb-a5d9-9e2b73d30afe'; +var channelID = '264c2ba0-f6db-4e84-acdb-bd225b62d9e3'; function run_test() { do_get_profile(); @@ -26,7 +26,6 @@ add_task(function* test_register_no_id() { PushServiceWebSocket._generateID = () => channelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { diff --git a/dom/push/test/xpcshell/test_register_request_queue.js b/dom/push/test/xpcshell/test_register_request_queue.js index b95917a2da..b44968129e 100644 --- a/dom/push/test/xpcshell/test_register_request_queue.js +++ b/dom/push/test/xpcshell/test_register_request_queue.js @@ -30,7 +30,6 @@ add_task(function* test_register_request_queue() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_register_rollback.js b/dom/push/test/xpcshell/test_register_rollback.js index 0e0baeaccb..0c7d22fedb 100644 --- a/dom/push/test/xpcshell/test_register_rollback.js +++ b/dom/push/test/xpcshell/test_register_rollback.js @@ -29,7 +29,6 @@ add_task(function* test_register_rollback() { PushServiceWebSocket._generateID = () => channelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db: makeStub(db, { put(prev, record) { return Promise.reject('universe has imploded'); diff --git a/dom/push/test/xpcshell/test_register_success.js b/dom/push/test/xpcshell/test_register_success.js index 68ead06637..6a6f146b79 100644 --- a/dom/push/test/xpcshell/test_register_success.js +++ b/dom/push/test/xpcshell/test_register_success.js @@ -25,7 +25,6 @@ add_task(function* test_register_success() { PushServiceWebSocket._generateID = () => channelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_register_timeout.js b/dom/push/test/xpcshell/test_register_timeout.js index fd5cd7688c..9dfb68a31b 100644 --- a/dom/push/test/xpcshell/test_register_timeout.js +++ b/dom/push/test/xpcshell/test_register_timeout.js @@ -29,7 +29,6 @@ add_task(function* test_register_timeout() { PushServiceWebSocket._generateID = () => channelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_register_wrong_id.js b/dom/push/test/xpcshell/test_register_wrong_id.js index fecab5e70a..014141200f 100644 --- a/dom/push/test/xpcshell/test_register_wrong_id.js +++ b/dom/push/test/xpcshell/test_register_wrong_id.js @@ -28,7 +28,6 @@ add_task(function* test_register_wrong_id() { PushServiceWebSocket._generateID = () => clientChannelID; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { diff --git a/dom/push/test/xpcshell/test_register_wrong_type.js b/dom/push/test/xpcshell/test_register_wrong_type.js index 12f6d9687d..035c1d11e5 100644 --- a/dom/push/test/xpcshell/test_register_wrong_type.js +++ b/dom/push/test/xpcshell/test_register_wrong_type.js @@ -24,7 +24,6 @@ add_task(function* test_register_wrong_type() { PushService._generateID = () => '1234'; PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { diff --git a/dom/push/test/xpcshell/test_registration_error.js b/dom/push/test/xpcshell/test_registration_error.js index 2619f46a05..d828cf5b58 100644 --- a/dom/push/test/xpcshell/test_registration_error.js +++ b/dom/push/test/xpcshell/test_registration_error.js @@ -19,7 +19,6 @@ add_task(function* test_registrations_error() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db: makeStub(db, { getByIdentifiers(prev, scope) { return Promise.reject('Database error'); diff --git a/dom/push/test/xpcshell/test_registration_error_http2.js b/dom/push/test/xpcshell/test_registration_error_http2.js index 22778d3288..ba42a3a9fe 100644 --- a/dom/push/test/xpcshell/test_registration_error_http2.js +++ b/dom/push/test/xpcshell/test_registration_error_http2.js @@ -16,7 +16,6 @@ add_task(function* test_registrations_error() { PushService.init({ serverURI: "https://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db: makeStub(db, { getByIdentifiers() { return Promise.reject('Database error'); diff --git a/dom/push/test/xpcshell/test_registration_missing_scope.js b/dom/push/test/xpcshell/test_registration_missing_scope.js index 868fbc159f..a30fad9ebd 100644 --- a/dom/push/test/xpcshell/test_registration_missing_scope.js +++ b/dom/push/test/xpcshell/test_registration_missing_scope.js @@ -14,7 +14,6 @@ function run_test() { add_task(function* test_registration_missing_scope() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri); } diff --git a/dom/push/test/xpcshell/test_registration_none.js b/dom/push/test/xpcshell/test_registration_none.js index 8186b8007a..0ca1150e47 100644 --- a/dom/push/test/xpcshell/test_registration_none.js +++ b/dom/push/test/xpcshell/test_registration_none.js @@ -17,7 +17,6 @@ function run_test() { add_task(function* test_registration_none() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri); } diff --git a/dom/push/test/xpcshell/test_registration_success.js b/dom/push/test/xpcshell/test_registration_success.js index 6f91261d33..6be11c56b5 100644 --- a/dom/push/test/xpcshell/test_registration_success.js +++ b/dom/push/test/xpcshell/test_registration_success.js @@ -48,7 +48,6 @@ add_task(function* test_registration_success() { let handshakePromise = new Promise(resolve => handshakeDone = resolve); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { diff --git a/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js b/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js index aa80c87b15..17db69f0e9 100644 --- a/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js +++ b/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js @@ -14,15 +14,18 @@ XPCOMUtils.defineLazyGetter(this, "serverPort", function() { return httpServer.identity.primaryPort; }); +var handlerDone; +var handlerPromise = new Promise(r => handlerDone = after(3, r)); + function listen4xxCodeHandler(metadata, response) { ok(true, "Listener point error") - do_test_finished(); + handlerDone(); response.setStatusLine(metadata.httpVersion, 410, "GONE"); } function resubscribeHandler(metadata, response) { ok(true, "Ask for new subscription"); - do_test_finished(); + handlerDone(); response.setHeader("Location", 'http://localhost:' + serverPort + '/newSubscription') response.setHeader("Link", @@ -33,7 +36,7 @@ function resubscribeHandler(metadata, response) { function listenSuccessHandler(metadata, response) { do_check_true(true, "New listener point"); - httpServer.stop(do_test_finished); + httpServer.stop(handlerDone); response.setStatusLine(metadata.httpVersion, 204, "Try again"); } @@ -64,10 +67,6 @@ add_task(function* test1() { return db.drop().then(_ => db.close()); }); - do_test_pending(); - do_test_pending(); - do_test_pending(); - var serverURL = "http://localhost:" + httpServer.identity.primaryPort; let records = [{ @@ -88,4 +87,17 @@ add_task(function* test1() { db }); + yield handlerPromise; + + let record = yield db.getByIdentifiers({ + scope: 'https://example.com/page', + originAttributes: '', + }); + equal(record.keyID, serverURL + '/newSubscription', + 'Should update subscription URL'); + equal(record.pushEndpoint, serverURL + '/newPushEndpoint', + 'Should update push endpoint'); + equal(record.pushReceiptEndpoint, serverURL + '/newReceiptPushEndpoint', + 'Should update push receipt endpoint'); + }); diff --git a/dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js b/dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js index 3059b8c094..bbe634d90a 100644 --- a/dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js +++ b/dom/push/test/xpcshell/test_resubscribe_5xxCode_http2.js @@ -14,11 +14,13 @@ XPCOMUtils.defineLazyGetter(this, "serverPort", function() { return httpServer.identity.primaryPort; }); -var retries = 0 +var retries = 0; +var handlerDone; +var handlerPromise = new Promise(r => handlerDone = after(5, r)); function listen5xxCodeHandler(metadata, response) { ok(true, "Listener 5xx code"); - do_test_finished(); + handlerDone(); retries++; response.setHeader("Retry-After", '1'); response.setStatusLine(metadata.httpVersion, 500, "Retry"); @@ -27,7 +29,7 @@ function listen5xxCodeHandler(metadata, response) { function resubscribeHandler(metadata, response) { ok(true, "Ask for new subscription"); ok(retries == 3, "Should retry 2 times."); - do_test_finished(); + handlerDone(); response.setHeader("Location", 'http://localhost:' + serverPort + '/newSubscription') response.setHeader("Link", @@ -38,7 +40,7 @@ function resubscribeHandler(metadata, response) { function listenSuccessHandler(metadata, response) { do_check_true(true, "New listener point"); - httpServer.stop(do_test_finished); + httpServer.stop(handlerDone); response.setStatusLine(metadata.httpVersion, 204, "Try again"); } @@ -68,12 +70,6 @@ add_task(function* test1() { return db.drop().then(_ => db.close()); }); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - do_test_pending(); - var serverURL = "http://localhost:" + httpServer.identity.primaryPort; let records = [{ @@ -94,4 +90,17 @@ add_task(function* test1() { db }); + yield handlerPromise; + + let record = yield db.getByIdentifiers({ + scope: 'https://example.com/page', + originAttributes: '', + }); + equal(record.keyID, serverURL + '/newSubscription', + 'Should update subscription URL'); + equal(record.pushEndpoint, serverURL + '/newPushEndpoint', + 'Should update push endpoint'); + equal(record.pushReceiptEndpoint, serverURL + '/newReceiptPushEndpoint', + 'Should update push receipt endpoint'); + }); diff --git a/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js b/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js index 137f5088c6..660e27f116 100644 --- a/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js +++ b/dom/push/test/xpcshell/test_resubscribe_listening_for_msg_error_http2.js @@ -14,9 +14,12 @@ XPCOMUtils.defineLazyGetter(this, "serverPort", function() { return httpServer.identity.primaryPort; }); +var handlerDone; +var handlerPromise = new Promise(r => handlerDone = after(2, r)); + function resubscribeHandler(metadata, response) { ok(true, "Ask for new subscription"); - do_test_finished(); + handlerDone(); response.setHeader("Location", 'http://localhost:' + serverPort + '/newSubscription') response.setHeader("Link", @@ -27,7 +30,7 @@ function resubscribeHandler(metadata, response) { function listenSuccessHandler(metadata, response) { do_check_true(true, "New listener point"); - httpServer.stop(do_test_finished); + httpServer.stop(handlerDone); response.setStatusLine(metadata.httpVersion, 204, "Try again"); } @@ -56,9 +59,6 @@ add_task(function* test1() { return db.drop().then(_ => db.close()); }); - do_test_pending(); - do_test_pending(); - var serverURL = "http://localhost:" + httpServer.identity.primaryPort; let records = [{ @@ -89,4 +89,17 @@ add_task(function* test1() { db }); + yield handlerPromise; + + let record = yield db.getByIdentifiers({ + scope: 'https://example.com/page', + originAttributes: '', + }); + equal(record.keyID, serverURL + '/newSubscription', + 'Should update subscription URL'); + equal(record.pushEndpoint, serverURL + '/newPushEndpoint', + 'Should update push endpoint'); + equal(record.pushReceiptEndpoint, serverURL + '/newReceiptPushEndpoint', + 'Should update push receipt endpoint'); + }); diff --git a/dom/push/test/xpcshell/test_retry_ws.js b/dom/push/test/xpcshell/test_retry_ws.js index e81e54c7dd..05f2616298 100644 --- a/dom/push/test/xpcshell/test_retry_ws.js +++ b/dom/push/test/xpcshell/test_retry_ws.js @@ -47,7 +47,6 @@ add_task(function* test_ws_retry() { let handshakePromise = new Promise(resolve => handshakeDone = resolve); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { diff --git a/dom/push/test/xpcshell/test_service_child.js b/dom/push/test/xpcshell/test_service_child.js index ab313e040f..8426936b84 100644 --- a/dom/push/test/xpcshell/test_service_child.js +++ b/dom/push/test/xpcshell/test_service_child.js @@ -45,6 +45,7 @@ add_test(function test_subscribe_success() { Services.scriptSecurityManager.getSystemPrincipal(), (result, subscription) => { ok(Components.isSuccessCode(result), 'Error creating subscription'); + ok(subscription.isSystemSubscription, 'Expected system subscription'); ok(subscription.endpoint.startsWith('https://example.org/push'), 'Wrong endpoint prefix'); equal(subscription.pushCount, 0, 'Wrong push count'); equal(subscription.lastPush, 0, 'Wrong last push time'); @@ -240,6 +241,8 @@ add_test(function test_subscribe_app_principal() { ok(Components.isSuccessCode(result), 'Error creating subscription'); ok(subscription.endpoint.startsWith('https://example.org/push'), 'Wrong push endpoint in app subscription'); + ok(!subscription.isSystemSubscription, + 'Unexpected system subscription for app principal'); equal(subscription.quota, 16, 'Wrong quota for app subscription'); do_test_finished(); @@ -256,6 +259,8 @@ add_test(function test_subscribe_origin_principal() { PushServiceComponent.subscribe(scope, principal, (result, subscription) => { ok(Components.isSuccessCode(result), 'Expected error creating subscription with origin principal'); + ok(!subscription.isSystemSubscription, + 'Unexpected system subscription for origin principal'); equal(subscription.quota, 16, 'Wrong quota for origin subscription'); do_test_finished(); @@ -270,9 +275,9 @@ add_test(function test_subscribe_null_principal() { Services.scriptSecurityManager.createNullPrincipal({}), (result, subscription) => { ok(!Components.isSuccessCode(result), - 'Expected error creating subscription with expanded principal'); + 'Expected error creating subscription with null principal'); strictEqual(subscription, null, - 'Unexpected subscription with expanded principal'); + 'Unexpected subscription with null principal'); do_test_finished(); run_next_test(); diff --git a/dom/push/test/xpcshell/test_startup_error.js b/dom/push/test/xpcshell/test_startup_error.js index f042cade36..b01b8a917b 100644 --- a/dom/push/test/xpcshell/test_startup_error.js +++ b/dom/push/test/xpcshell/test_startup_error.js @@ -14,7 +14,6 @@ add_task(function* test_startup_error() { PushService.init({ serverURI: 'wss://push.example.org/', - networkInfo: new MockDesktopNetworkInfo(), db: makeStub(db, { getAllExpired(prev) { return Promise.reject('database corruption on startup'); @@ -45,7 +44,6 @@ add_task(function* test_startup_error() { PushService.init({ serverURI: 'wss://push.example.org/', - networkInfo: new MockDesktopNetworkInfo(), db: makeStub(db, { getAllUnexpired(prev) { return Promise.reject('database corruption on connect'); diff --git a/dom/push/test/xpcshell/test_unregister_empty_scope.js b/dom/push/test/xpcshell/test_unregister_empty_scope.js index 75c89dfea3..aad51948a5 100644 --- a/dom/push/test/xpcshell/test_unregister_empty_scope.js +++ b/dom/push/test/xpcshell/test_unregister_empty_scope.js @@ -14,7 +14,6 @@ function run_test() { add_task(function* test_unregister_empty_scope() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { diff --git a/dom/push/test/xpcshell/test_unregister_error.js b/dom/push/test/xpcshell/test_unregister_error.js index 58be1cbde6..53d5929185 100644 --- a/dom/push/test/xpcshell/test_unregister_error.js +++ b/dom/push/test/xpcshell/test_unregister_error.js @@ -29,7 +29,6 @@ add_task(function* test_unregister_error() { let unregisterPromise = new Promise(resolve => unregisterDone = resolve); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_unregister_invalid_json.js b/dom/push/test/xpcshell/test_unregister_invalid_json.js index f777c1f657..1f7e8267b2 100644 --- a/dom/push/test/xpcshell/test_unregister_invalid_json.js +++ b/dom/push/test/xpcshell/test_unregister_invalid_json.js @@ -43,7 +43,6 @@ add_task(function* test_unregister_invalid_json() { let unregisterPromise = new Promise(resolve => unregisterDone = after(2, resolve)); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_unregister_not_found.js b/dom/push/test/xpcshell/test_unregister_not_found.js index f74d29097b..79cc122d08 100644 --- a/dom/push/test/xpcshell/test_unregister_not_found.js +++ b/dom/push/test/xpcshell/test_unregister_not_found.js @@ -14,7 +14,6 @@ function run_test() { add_task(function* test_unregister_not_found() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), makeWebSocket(uri) { return new MockWebSocket(uri, { onHello(request) { diff --git a/dom/push/test/xpcshell/test_unregister_success.js b/dom/push/test/xpcshell/test_unregister_success.js index 00567742ea..ba4333d915 100644 --- a/dom/push/test/xpcshell/test_unregister_success.js +++ b/dom/push/test/xpcshell/test_unregister_success.js @@ -29,7 +29,6 @@ add_task(function* test_unregister_success() { let unregisterPromise = new Promise(resolve => unregisterDone = resolve); PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/test_updateRecordNoEncryptionKeys_ws.js b/dom/push/test/xpcshell/test_updateRecordNoEncryptionKeys_ws.js index 353cece050..be6a25e2ba 100644 --- a/dom/push/test/xpcshell/test_updateRecordNoEncryptionKeys_ws.js +++ b/dom/push/test/xpcshell/test_updateRecordNoEncryptionKeys_ws.js @@ -41,7 +41,6 @@ add_task(function* test_with_data_enabled() { PushService.init({ serverURI: "wss://push.example.org/", - networkInfo: new MockDesktopNetworkInfo(), db, makeWebSocket(uri) { return new MockWebSocket(uri, { diff --git a/dom/push/test/xpcshell/xpcshell.ini b/dom/push/test/xpcshell/xpcshell.ini index 71638389b7..76d8b85430 100644 --- a/dom/push/test/xpcshell/xpcshell.ini +++ b/dom/push/test/xpcshell/xpcshell.ini @@ -16,6 +16,7 @@ support-files = PushServiceHandler.js PushServiceHandler.manifest [test_notification_error.js] [test_notification_incomplete.js] [test_notification_version_string.js] +[test_observer_data.js] [test_permissions.js] run-sequentially = This will delete all existing push subscriptions. diff --git a/dom/security/nsCSPContext.cpp b/dom/security/nsCSPContext.cpp index 0f7e3e113f..0f7ab7dd92 100644 --- a/dom/security/nsCSPContext.cpp +++ b/dom/security/nsCSPContext.cpp @@ -727,13 +727,15 @@ StripURIForReporting(nsIURI* aURI, // 1) If the origin of uri is a globally unique identifier (for example, // aURI has a scheme of data, blob, or filesystem), then return the // ASCII serialization of uri’s scheme. - bool isHttp = - (NS_SUCCEEDED(aURI->SchemeIs("http", &isHttp)) && isHttp) || - (NS_SUCCEEDED(aURI->SchemeIs("https", &isHttp)) && isHttp); - if (!isHttp) { + bool isHttpOrFtp = + (NS_SUCCEEDED(aURI->SchemeIs("http", &isHttpOrFtp)) && isHttpOrFtp) || + (NS_SUCCEEDED(aURI->SchemeIs("https", &isHttpOrFtp)) && isHttpOrFtp) || + (NS_SUCCEEDED(aURI->SchemeIs("ftp", &isHttpOrFtp)) && isHttpOrFtp); + + if (!isHttpOrFtp) { // not strictly spec compliant, but what we really care about is - // http/https. If it's not http/https, then treat aURI as if - // it's a globally unique identifier and just return the scheme. + // http/https and also ftp. If it's not http/https or ftp, then treat aURI + // as if it's a globally unique identifier and just return the scheme. aURI->GetScheme(outStrippedURI); return; } @@ -966,7 +968,7 @@ nsCSPContext::SendReports(nsISupports* aBlockedContentSource, continue; } - rv = uploadChannel->SetUploadStream(sis, NS_LITERAL_CSTRING("application/json"), -1); + rv = uploadChannel->SetUploadStream(sis, NS_LITERAL_CSTRING("application/csp-report"), -1); NS_ENSURE_SUCCESS(rv, rv); // if this is an HTTP channel, set the request method to post diff --git a/dom/security/nsCSPParser.cpp b/dom/security/nsCSPParser.cpp index 2a6574155a..a41895d3e1 100644 --- a/dom/security/nsCSPParser.cpp +++ b/dom/security/nsCSPParser.cpp @@ -38,7 +38,6 @@ static const char16_t DOT = '.'; static const char16_t UNDERLINE = '_'; static const char16_t TILDE = '~'; static const char16_t WILDCARD = '*'; -static const char16_t WHITESPACE = ' '; static const char16_t SINGLEQUOTE = '\''; static const char16_t OPEN_CURL = '{'; static const char16_t CLOSE_CURL = '}'; @@ -78,7 +77,7 @@ nsCSPTokenizer::generateNextToken() { skipWhiteSpaceAndSemicolon(); while (!atEnd() && - *mCurChar != WHITESPACE && + !nsContentUtils::IsHTMLWhitespace(*mCurChar) && *mCurChar != SEMICOLON) { mCurToken.Append(*mCurChar++); } @@ -122,7 +121,7 @@ nsCSPParser::nsCSPParser(cspTokens& aTokens, nsIURI* aSelfURI, nsCSPContext* aCSPContext, bool aDeliveredViaMetaTag) - : mCurChar(nullptr) + : mCurChar(nullptr) , mEndChar(nullptr) , mHasHashOrNonce(false) , mUnsafeInlineKeywordSrc(nullptr) @@ -1121,15 +1120,16 @@ nsCSPParser::directive() srcs.AppendElement(keyword); } - // if a hash or nonce is specified within script-src, then - // unsafe-inline should be ignored, see: + // Ignore unsafe-inline within script-src or style-src if nonce + // or hash is specified, see: // http://www.w3.org/TR/CSP2/#directive-script-src - if (cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) && + if ((cspDir->equals(nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE) || + cspDir->equals(nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE)) && mHasHashOrNonce && mUnsafeInlineKeywordSrc) { mUnsafeInlineKeywordSrc->invalidate(); // log to the console that unsafe-inline will be ignored const char16_t* params[] = { MOZ_UTF16("'unsafe-inline'") }; - logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptSrc", + logWarningErrorToConsole(nsIScriptError::warningFlag, "ignoringSrcWithinScriptStyleSrc", params, ArrayLength(params)); } diff --git a/dom/security/nsCSPParser.h b/dom/security/nsCSPParser.h index 73ca343c73..5e628726e9 100644 --- a/dom/security/nsCSPParser.h +++ b/dom/security/nsCSPParser.h @@ -54,7 +54,8 @@ class nsCSPTokenizer { inline void skipWhiteSpace() { - while (mCurChar < mEndChar && *mCurChar == ' ') { + while (mCurChar < mEndChar && + nsContentUtils::IsHTMLWhitespace(*mCurChar)) { mCurToken.Append(*mCurChar++); } mCurToken.Truncate(); @@ -62,7 +63,8 @@ class nsCSPTokenizer { inline void skipWhiteSpaceAndSemicolon() { - while (mCurChar < mEndChar && (*mCurChar == ' ' || *mCurChar == ';')) { + while (mCurChar < mEndChar && (*mCurChar == ';' || + nsContentUtils::IsHTMLWhitespace(*mCurChar))){ mCurToken.Append(*mCurChar++); } mCurToken.Truncate(); diff --git a/dom/security/nsMixedContentBlocker.cpp b/dom/security/nsMixedContentBlocker.cpp index 6b523ac746..55c32a517d 100644 --- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -434,7 +434,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, // content websockets within the websockets implementation, so we don't need // to do any blocking here, nor do we need to provide a way to undo or // override the blocking. Websockets without TLS are very flaky anyway in the - // face of many HTTP-aware proxies. Compared to psasive content, there is + // face of many HTTP-aware proxies. Compared to passive content, there is // additional risk that the script using WebSockets will disclose sensitive // information from the HTTPS page and/or eval (directly or indirectly) // received data. @@ -500,6 +500,17 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, break; } + // Make sure to get the URI the load started with. No need to check + // outer schemes because all the wrapping pseudo protocols inherit the + // security properties of the actual network request represented + // by the innerMost URL. + nsCOMPtr innerContentLocation = NS_GetInnermostURI(aContentLocation); + if (!innerContentLocation) { + NS_ERROR("Can't get innerURI from aContentLocation"); + *aDecision = REJECT_REQUEST; + return NS_OK; + } + /* Get the scheme of the sub-document resource to be requested. If it is * a safe to load in an https context then mixed content doesn't apply. * @@ -521,16 +532,16 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, bool schemeNoReturnData = false; bool schemeInherits = false; bool schemeSecure = false; - if (NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) || - NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) || - NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) || - NS_FAILED(NS_URIChainHasFlags(aContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) { + if (NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE , &schemeLocal)) || + NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &schemeNoReturnData)) || + NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, &schemeInherits)) || + NS_FAILED(NS_URIChainHasFlags(innerContentLocation, nsIProtocolHandler::URI_SAFE_TO_LOAD_IN_SECURE_CONTEXT, &schemeSecure))) { *aDecision = REJECT_REQUEST; return NS_ERROR_FAILURE; } // TYPE_IMAGE redirects are cached based on the original URI, not the final // destination and hence cache hits for images may not have the correct - // aContentLocation. Check if the cached hit went through an http redirect, + // innerContentLocation. Check if the cached hit went through an http redirect, // and if it did, we can't treat this as a secure subresource. if (!aHadInsecureImageRedirect && (schemeLocal || schemeNoReturnData || schemeInherits || schemeSecure)) { @@ -614,14 +625,14 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, // Check the parent scheme. If it is not an HTTPS page then mixed content // restrictions do not apply. bool parentIsHttps; - nsCOMPtr innerURI = NS_GetInnermostURI(requestingLocation); - if (!innerURI) { + nsCOMPtr innerRequestingLocation = NS_GetInnermostURI(requestingLocation); + if (!innerRequestingLocation) { NS_ERROR("Can't get innerURI from requestingLocation"); *aDecision = REJECT_REQUEST; return NS_OK; } - nsresult rv = innerURI->SchemeIs("https", &parentIsHttps); + nsresult rv = innerRequestingLocation->SchemeIs("https", &parentIsHttps); if (NS_FAILED(rv)) { NS_ERROR("requestingLocation->SchemeIs failed"); *aDecision = REJECT_REQUEST; @@ -665,12 +676,12 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, // workers. if (isWorkerType) { // For workers, we can assume that we're mixed content at this point, since - // the parent is https, and the protocol associated with aContentLocation + // the parent is https, and the protocol associated with innerContentLocation // doesn't map to the secure URI flags checked above. Assert this for // sanity's sake #ifdef DEBUG bool isHttpsScheme = false; - rv = aContentLocation->SchemeIs("https", &isHttpsScheme); + rv = innerContentLocation->SchemeIs("https", &isHttpsScheme); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(!isHttpsScheme); #endif @@ -690,7 +701,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, // subresource load uses http: and the CSP directive 'upgrade-insecure-requests' // is present on the page. bool isHttpScheme = false; - rv = aContentLocation->SchemeIs("http", &isHttpScheme); + rv = innerContentLocation->SchemeIs("http", &isHttpScheme); NS_ENSURE_SUCCESS(rv, rv); if (isHttpScheme && docShell->GetDocument()->GetUpgradeInsecureRequests(isPreload)) { *aDecision = ACCEPT; @@ -787,13 +798,13 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, bool active = (classification == eMixedScript); if (!aHadInsecureImageRedirect) { if (XRE_IsParentProcess()) { - AccumulateMixedContentHSTS(aContentLocation, active); + AccumulateMixedContentHSTS(innerContentLocation, active); } else { // Ask the parent process to do the same call mozilla::dom::ContentChild* cc = mozilla::dom::ContentChild::GetSingleton(); if (cc) { mozilla::ipc::URIParams uri; - SerializeURI(aContentLocation, uri); + SerializeURI(innerContentLocation, uri); cc->SendAccumulateMixedContentHSTS(uri, active); } } @@ -910,9 +921,6 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect, *aDecision = ACCEPT; return NS_OK; } - - *aDecision = REJECT_REQUEST; - return NS_OK; } NS_IMETHODIMP diff --git a/dom/security/test/unit/test_csp_reports.js b/dom/security/test/unit/test_csp_reports.js index 1d55a92e41..0bdc786d1d 100644 --- a/dom/security/test/unit/test_csp_reports.js +++ b/dom/security/test/unit/test_csp_reports.js @@ -172,4 +172,13 @@ function run_test() { 4); } }); + + // test scheme of ftp: + makeTest(8, {"blocked-uri": "ftp://blocked.test"}, false, + function(csp) { + // shouldLoad creates and sends out the report here. + csp.shouldLoad(Ci.nsIContentPolicy.TYPE_SCRIPT, + NetUtil.newURI("ftp://blocked.test/profile.png"), + null, null, null, null); + }); } diff --git a/dom/settings/SettingsDB.jsm b/dom/settings/SettingsDB.jsm index e39f466192..93a8b3738d 100644 --- a/dom/settings/SettingsDB.jsm +++ b/dom/settings/SettingsDB.jsm @@ -40,7 +40,7 @@ const TYPED_ARRAY_THINGS = new Set([ ]); this.SETTINGSDB_NAME = "settings"; -this.SETTINGSDB_VERSION = 5; +this.SETTINGSDB_VERSION = 7; this.SETTINGSSTORE_NAME = "settings"; Cu.import("resource://gre/modules/IndexedDBHelper.jsm"); diff --git a/dom/settings/SettingsManager.js b/dom/settings/SettingsManager.js index 6d17e17900..a73e5f3127 100644 --- a/dom/settings/SettingsManager.js +++ b/dom/settings/SettingsManager.js @@ -381,6 +381,7 @@ SettingsManager.prototype = { debug("WARNING: MORE THAN " + kObserverSoftLimit + " OBSERVERS FOR " + aName + ": " + length + " FROM" + (new Error).stack); #ifdef DEBUG + debug("JS STOPS EXECUTING AT THIS POINT IN DEBUG BUILDS!"); throw Components.results.NS_ERROR_ABORT; #endif } diff --git a/dom/settings/SettingsRequestManager.jsm b/dom/settings/SettingsRequestManager.jsm index 388c936820..fced802472 100644 --- a/dom/settings/SettingsRequestManager.jsm +++ b/dom/settings/SettingsRequestManager.jsm @@ -40,7 +40,7 @@ function debug(s) { dump("-*- SettingsRequestManager: " + s + "\n"); } -let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime) +var inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime) .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; const kXpcomShutdownObserverTopic = "xpcom-shutdown"; @@ -58,8 +58,12 @@ const kAllSettingsWritePermission = "settings" + kSettingsWriteSuffix; // will be allowed depends on the exact permissions the app has. const kSomeSettingsReadPermission = "settings-api" + kSettingsReadSuffix; const kSomeSettingsWritePermission = "settings-api" + kSettingsWriteSuffix; + // Time, in seconds, to consider the API is starting to jam -const kSoftLockupDelta = 30; +var kSoftLockupDelta = 30; +try { + kSoftLockupDelta = Services.prefs.getIntPref("dom.mozSettings.softLockupDelta"); +} catch (ex) { } XPCOMUtils.defineLazyServiceGetter(this, "mrm", "@mozilla.org/memory-reporter-manager;1", diff --git a/dom/settings/tests/file_loadserver.js b/dom/settings/tests/file_loadserver.js index 64c2104a92..a919d9690a 100644 --- a/dom/settings/tests/file_loadserver.js +++ b/dom/settings/tests/file_loadserver.js @@ -1,9 +1,9 @@ -let Ci = Components.interfaces; -let Cc = Components.classes; -let Cu = Components.utils; +var Ci = Components.interfaces; +var Cc = Components.classes; +var Cu = Components.utils; // Stolen from SpecialPowers, since at this point we don't know we're in a test. -let isMainProcess = function() { +var isMainProcess = function() { try { return Cc["@mozilla.org/xre/app-info;1"]. getService(Ci.nsIXULRuntime). diff --git a/dom/settings/tests/test_settings_service.js b/dom/settings/tests/test_settings_service.js index f04cf9a155..132877a5d4 100644 --- a/dom/settings/tests/test_settings_service.js +++ b/dom/settings/tests/test_settings_service.js @@ -1,8 +1,8 @@ "use strict"; -const Cu = Components.utils; -const Cc = Components.classes; -const Ci = Components.interfaces; +var Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; if (SpecialPowers.isMainProcess()) { SpecialPowers.Cu.import("resource://gre/modules/SettingsRequestManager.jsm"); diff --git a/dom/settings/tests/test_settings_service_callback.js b/dom/settings/tests/test_settings_service_callback.js index b855fad118..a780bb9c32 100644 --- a/dom/settings/tests/test_settings_service_callback.js +++ b/dom/settings/tests/test_settings_service_callback.js @@ -1,8 +1,8 @@ "use strict"; -const Cu = Components.utils; -const Cc = Components.classes; -const Ci = Components.interfaces; +var Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -13,7 +13,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "SettingsService", "@mozilla.org/settingsService;1", "nsISettingsService"); -let tests = [ +var tests = [ function () { let callback = { handle: function() { diff --git a/dom/settings/tests/unit/test_settingsrequestmanager_messages.js b/dom/settings/tests/unit/test_settingsrequestmanager_messages.js index 427af3aeb9..e5fb084755 100644 --- a/dom/settings/tests/unit/test_settingsrequestmanager_messages.js +++ b/dom/settings/tests/unit/test_settingsrequestmanager_messages.js @@ -1,6 +1,6 @@ "use strict"; -const Cu = Components.utils; +var Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); @@ -9,8 +9,8 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm", "@mozilla.org/childprocessmessagemanager;1", "nsIMessageSender"); -let principal = Services.scriptSecurityManager.getSystemPrincipal(); -let lockID = "{435d2192-4f21-48d4-90b7-285f147a56be}"; +var principal = Services.scriptSecurityManager.getSystemPrincipal(); +var lockID = "{435d2192-4f21-48d4-90b7-285f147a56be}"; // Helper to start the Settings Request Manager function startSettingsRequestManager() { diff --git a/dom/simplepush/moz.build b/dom/simplepush/moz.build index 0d6805f3a9..96b273fa81 100644 --- a/dom/simplepush/moz.build +++ b/dom/simplepush/moz.build @@ -12,3 +12,7 @@ EXTRA_COMPONENTS += [ EXTRA_JS_MODULES += [ 'PushService.jsm', ] + +MOCHITEST_MANIFESTS += [ + 'test/mochitest.ini', +] diff --git a/dom/simplepush/test/mochitest.ini b/dom/simplepush/test/mochitest.ini new file mode 100644 index 0000000000..ee9b7cb450 --- /dev/null +++ b/dom/simplepush/test/mochitest.ini @@ -0,0 +1,6 @@ +[DEFAULT] +run-if = toolkit == "gonk" + +[test_permissions.html] +[test_register.html] +[test_prefs.html] diff --git a/dom/simplepush/test/test_permissions.html b/dom/simplepush/test/test_permissions.html new file mode 100644 index 0000000000..f925c2732f --- /dev/null +++ b/dom/simplepush/test/test_permissions.html @@ -0,0 +1,60 @@ + + + + + Test for Bug 1235050 + + + + +Mozilla Bug 1235050 +

+ +
+
+ + + + diff --git a/dom/simplepush/test/test_prefs.html b/dom/simplepush/test/test_prefs.html new file mode 100644 index 0000000000..0ef9678e30 --- /dev/null +++ b/dom/simplepush/test/test_prefs.html @@ -0,0 +1,63 @@ + + + + + Test for Bug 1235050 + + + + +Mozilla Bug 1235050 +

+ +
+
+ + + + diff --git a/dom/simplepush/test/test_register.html b/dom/simplepush/test/test_register.html new file mode 100644 index 0000000000..989aa9657e --- /dev/null +++ b/dom/simplepush/test/test_register.html @@ -0,0 +1,57 @@ + + + + + Test for Bug 1235050 + + + + +Mozilla Bug 1235050 +

+ +
+
+ + + + diff --git a/dom/smil/crashtests/483584-2.svg b/dom/smil/crashtests/483584-2.svg index 8671a2c9e0..f5cbd7d466 100644 --- a/dom/smil/crashtests/483584-2.svg +++ b/dom/smil/crashtests/483584-2.svg @@ -7,8 +7,24 @@ - + + @@ -45,73 +61,73 @@ - + - - + + - - + + - - + + - + - + - + - - + + - + - + - - + + - + $Revision: 1.6 $ - \ No newline at end of file + diff --git a/dom/smil/test/test_smilTimeEvents.xhtml b/dom/smil/test/test_smilTimeEvents.xhtml index f1e26fe82e..fb5b54ddbb 100644 --- a/dom/smil/test/test_smilTimeEvents.xhtml +++ b/dom/smil/test/test_smilTimeEvents.xhtml @@ -32,7 +32,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=572270 /** Test SMIL TimeEvents dispatching **/ /* Global Variables */ -const gTimeoutDur = 5000; // Time until we give up waiting for events in ms +const gTimeoutDur = 60000; // Time until we give up waiting for events in ms var gSvg = document.getElementById("svg"); var gAnim = document.getElementById('anim'); var gCircle = document.getElementById('circle'); diff --git a/dom/telephony/test/xpcshell/header_helpers.js b/dom/telephony/test/xpcshell/header_helpers.js index 88ff90fb14..fee6392e01 100644 --- a/dom/telephony/test/xpcshell/header_helpers.js +++ b/dom/telephony/test/xpcshell/header_helpers.js @@ -5,5 +5,5 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; -let subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] +var subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"] .getService(Ci.mozIJSSubScriptLoader); diff --git a/dom/telephony/test/xpcshell/test_parseMMI.js b/dom/telephony/test/xpcshell/test_parseMMI.js index 74ffed2546..444eb12a87 100644 --- a/dom/telephony/test/xpcshell/test_parseMMI.js +++ b/dom/telephony/test/xpcshell/test_parseMMI.js @@ -1,10 +1,10 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ -let TS = {}; +var TS = {}; subscriptLoader.loadSubScript("resource://gre/components/TelephonyService.js", TS); -let NS = {}; +var NS = {}; subscriptLoader.loadSubScript("resource://gre/modules/DialNumberUtils.jsm", NS); function run_test() { diff --git a/dom/tests/manual/innertext_performance_test.html b/dom/tests/manual/innertext_performance_test.html deleted file mode 100644 index 4e8ee3be46..0000000000 --- a/dom/tests/manual/innertext_performance_test.html +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - -
Testing... please wait...0
-
 
-
 
- - - \ No newline at end of file diff --git a/dom/tests/unit/test_geolocation_position_unavailable.js b/dom/tests/unit/test_geolocation_position_unavailable.js index 77c1d47172..498a69d5e1 100644 --- a/dom/tests/unit/test_geolocation_position_unavailable.js +++ b/dom/tests/unit/test_geolocation_position_unavailable.js @@ -1,7 +1,7 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; function successCallback() { diff --git a/dom/tests/unit/test_geolocation_position_unavailable_wrap.js b/dom/tests/unit/test_geolocation_position_unavailable_wrap.js index 585c7511a7..4922ce5bde 100644 --- a/dom/tests/unit/test_geolocation_position_unavailable_wrap.js +++ b/dom/tests/unit/test_geolocation_position_unavailable_wrap.js @@ -1,6 +1,6 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; function run_test() { diff --git a/dom/tests/unit/test_geolocation_provider.js b/dom/tests/unit/test_geolocation_provider.js index 34d7803b0b..06451b6b98 100644 --- a/dom/tests/unit/test_geolocation_provider.js +++ b/dom/tests/unit/test_geolocation_provider.js @@ -1,7 +1,7 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; Cu.import("resource://testing-common/httpd.js"); diff --git a/dom/tests/unit/test_geolocation_provider_timeout.js b/dom/tests/unit/test_geolocation_provider_timeout.js index da5091e49a..d9078e5387 100644 --- a/dom/tests/unit/test_geolocation_provider_timeout.js +++ b/dom/tests/unit/test_geolocation_provider_timeout.js @@ -1,6 +1,6 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; Cu.import("resource://testing-common/httpd.js"); diff --git a/dom/tests/unit/test_geolocation_reset_accuracy.js b/dom/tests/unit/test_geolocation_reset_accuracy.js index 75272ab7a0..63e7135cc1 100644 --- a/dom/tests/unit/test_geolocation_reset_accuracy.js +++ b/dom/tests/unit/test_geolocation_reset_accuracy.js @@ -1,5 +1,5 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; +var Cc = Components.classes; +var Ci = Components.interfaces; const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}"); const providerContract = "@mozilla.org/geolocation/provider;1"; @@ -39,7 +39,7 @@ var provider = { _seenHigh: false }; -let runningInParent = true; +var runningInParent = true; try { runningInParent = Components.classes["@mozilla.org/xre/runtime;1"]. getService(Components.interfaces.nsIXULRuntime).processType diff --git a/dom/tests/unit/test_geolocation_reset_accuracy_wrap.js b/dom/tests/unit/test_geolocation_reset_accuracy_wrap.js index 84a4ac27db..b44a97e793 100644 --- a/dom/tests/unit/test_geolocation_reset_accuracy_wrap.js +++ b/dom/tests/unit/test_geolocation_reset_accuracy_wrap.js @@ -1,5 +1,5 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; +var Cc = Components.classes; +var Ci = Components.interfaces; const providerCID = Components.ID("{14aa4b81-e266-45cb-88f8-89595dece114}"); const providerContract = "@mozilla.org/geolocation/provider;1"; diff --git a/dom/tests/unit/test_geolocation_timeout.js b/dom/tests/unit/test_geolocation_timeout.js index a3f400b2e9..cfe0ba7375 100644 --- a/dom/tests/unit/test_geolocation_timeout.js +++ b/dom/tests/unit/test_geolocation_timeout.js @@ -1,7 +1,7 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -const Cr = Components.results; +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; +var Cr = Components.results; Cu.import("resource://testing-common/httpd.js"); diff --git a/dom/tests/unit/test_geolocation_timeout_wrap.js b/dom/tests/unit/test_geolocation_timeout_wrap.js index 514c3f9b3a..1f87bec7eb 100644 --- a/dom/tests/unit/test_geolocation_timeout_wrap.js +++ b/dom/tests/unit/test_geolocation_timeout_wrap.js @@ -1,6 +1,6 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; +var Cc = Components.classes; +var Ci = Components.interfaces; +var Cu = Components.utils; Cu.import("resource://testing-common/httpd.js"); diff --git a/dom/u2f/tests/test_util_methods.html b/dom/u2f/tests/test_util_methods.html index 45c33d3331..4cccdd2c86 100644 --- a/dom/u2f/tests/test_util_methods.html +++ b/dom/u2f/tests/test_util_methods.html @@ -4,6 +4,10 @@ Test for Utility Methods for other FIDO Universal Second Factor tests + + + + diff --git a/dom/u2f/tests/u2futil.js b/dom/u2f/tests/u2futil.js index 7e90b8f66c..b415ff6c81 100644 --- a/dom/u2f/tests/u2futil.js +++ b/dom/u2f/tests/u2futil.js @@ -130,29 +130,33 @@ function assembleRegistrationSignedData(appParam, challengeParam, keyHandle, pub return signedData; } -function verifySignature(key, data, derSig) { - if (derSig.byteLength < 70) { - console.log("bad sig: " + hexEncode(derSig)) - throw "Invalid signature length: " + derSig.byteLength; +function sanitizeSigArray(arr) { + // ECDSA signature fields into WebCrypto must be exactly 32 bytes long, so + // this method strips leading padding bytes, if added, and also appends + // padding zeros, if needed. + if (arr.length > 32) { + arr = arr.slice(arr.length - 32) } + var ret = new Uint8Array(32); + ret.set(arr, ret.length - arr.length); + return ret; +} - // Poor man's ASN.1 decode - // R and S are always 32 bytes. If ether has a DER - // length > 32, it's just zeros we can chop off. - var lenR = derSig[3]; - var lenS = derSig[3 + lenR + 2]; - var padR = lenR - 32; - var padS = lenS - 32; - var sig = new Uint8Array(64); - derSig.slice(4 + padR, 4 + lenR).map((x, i) => sig[i] = x); - derSig.slice(4 + lenR + 2 + padS, 4 + lenR + 2 + lenS).map( - (x, i) => sig[32 + i] = x - ); +function verifySignature(key, data, derSig) { + var sigAsn1 = org.pkijs.fromBER(derSig.buffer); + var sigR = new Uint8Array(sigAsn1.result.value_block.value[0].value_block.value_hex); + var sigS = new Uint8Array(sigAsn1.result.value_block.value[1].value_block.value_hex); - console.log("data: " + hexEncode(data)); - console.log("der: " + hexEncode(derSig)); - console.log("raw: " + hexEncode(sig)); + // The resulting R and S values from the ASN.1 Sequence must be fit into 32 + // bytes. Sometimes they have leading zeros, sometimes they're too short, it + // all depends on what lib generated the signature. + var R = sanitizeSigArray(sigR); + var S = sanitizeSigArray(sigS); + + var sigData = new Uint8Array(R.length + S.length); + sigData.set(R); + sigData.set(S, R.length); var alg = {name: "ECDSA", hash: "SHA-256"}; - return crypto.subtle.verify(alg, key, sig, data); + return crypto.subtle.verify(alg, key, sigData, data); } diff --git a/dom/webidl/OfflineAudioContext.webidl b/dom/webidl/OfflineAudioContext.webidl index 7e6ebeb00d..d126dae80e 100644 --- a/dom/webidl/OfflineAudioContext.webidl +++ b/dom/webidl/OfflineAudioContext.webidl @@ -19,5 +19,6 @@ interface OfflineAudioContext : AudioContext { Promise startRendering(); attribute EventHandler oncomplete; + readonly attribute unsigned long length; }; diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js index f5f78a13d5..908c9f3161 100644 --- a/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js +++ b/dom/workers/test/WorkerDebuggerGlobalScope.createSandbox_debugger.js @@ -2,8 +2,8 @@ const SANDBOX_URL = "WorkerDebuggerGlobalScope.createSandbox_sandbox.js"; -let prototype = { +var prototype = { self: this, }; -let sandbox = createSandbox(SANDBOX_URL, prototype); +var sandbox = createSandbox(SANDBOX_URL, prototype); loadSubScript(SANDBOX_URL, sandbox); diff --git a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js index 523d300bcf..dcd4dbecd1 100644 --- a/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js +++ b/dom/workers/test/WorkerDebuggerGlobalScope.enterEventLoop_debugger.js @@ -1,6 +1,6 @@ "use strict"; -let frames = []; +var frames = []; var dbg = new Debugger(global); dbg.onDebuggerStatement = function (frame) { diff --git a/dom/workers/test/serviceworkers/test_notification_get.html b/dom/workers/test/serviceworkers/test_notification_get.html index 822843c124..51a2163d5f 100644 --- a/dom/workers/test/serviceworkers/test_notification_get.html +++ b/dom/workers/test/serviceworkers/test_notification_get.html @@ -59,6 +59,40 @@ }); } + function testDismiss() { + // Dismissed persistent notifications should be removed from the + // notification list. + var alertsService = SpecialPowers.Cc["@mozilla.org/alerts-service;1"] + .getService(SpecialPowers.Ci.nsIAlertsService); + return navigator.serviceWorker.getRegistration("./notification/") + .then(function(reg) { + return reg.showNotification( + "This is a notification that will be closed", { tag: "dismiss" }) + .then(function() { + return reg; + }); + }).then(function(reg) { + return reg.getNotifications() + .then(function(notifications) { + is(notifications.length, 1, "There should be one visible notification"); + is(notifications[0].tag, "dismiss", "Tag should match"); + + // Simulate dismissing the notification by using the alerts service + // directly, instead of `Notification#close`. + var principal = SpecialPowers.wrap(document).nodePrincipal; + var id = principal.origin + "#tag:dismiss"; + alertsService.closeAlert(id, principal); + + return reg; + }); + }).then(function(reg) { + return reg.getNotifications(); + }).then(function(notifications) { + // Make sure dismissed notifications are no longer retrieved. + is(notifications.length, 0, "There should be no more stored notifications"); + }); + } + function testGet() { // Non persistent notifications will not show up in getNotification(). var n = new Notification("Scope does not match"); @@ -167,6 +201,7 @@ .then(testGetWorker) .then(testGetServiceWorker) .then(testAcrossThreads) + .then(testDismiss) .then(unregisterSW) .then(function() { MockServices.unregister(); diff --git a/dom/xslt/crashtests/1205163.xml b/dom/xslt/crashtests/1205163.xml new file mode 100644 index 0000000000..2d450a1d50 --- /dev/null +++ b/dom/xslt/crashtests/1205163.xml @@ -0,0 +1,6 @@ + + + + Example + Error + diff --git a/dom/xslt/crashtests/1205163.xsl b/dom/xslt/crashtests/1205163.xsl new file mode 100644 index 0000000000..f0bbb5cb95 --- /dev/null +++ b/dom/xslt/crashtests/1205163.xsl @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/dom/xslt/crashtests/1243337.xml b/dom/xslt/crashtests/1243337.xml new file mode 100644 index 0000000000..40f5e3a35d --- /dev/null +++ b/dom/xslt/crashtests/1243337.xml @@ -0,0 +1,3 @@ + + + diff --git a/dom/xslt/crashtests/1243337.xsl b/dom/xslt/crashtests/1243337.xsl new file mode 100644 index 0000000000..0e659bf900 --- /dev/null +++ b/dom/xslt/crashtests/1243337.xsl @@ -0,0 +1,6 @@ + + + + + + diff --git a/dom/xslt/crashtests/crashtests.list b/dom/xslt/crashtests/crashtests.list index 06daebc8a8..5958655d64 100644 --- a/dom/xslt/crashtests/crashtests.list +++ b/dom/xslt/crashtests/crashtests.list @@ -16,3 +16,5 @@ load 602115.html load 603844.html load 667315.xml load 1089049.html +load 1205163.xml +load 1243337.xml diff --git a/dom/xslt/tests/mochitest/file_bug1135764.xml b/dom/xslt/tests/mochitest/file_bug1135764.xml index fddb88fa96..b9da87e5e5 100644 --- a/dom/xslt/tests/mochitest/file_bug1135764.xml +++ b/dom/xslt/tests/mochitest/file_bug1135764.xml @@ -1,6 +1,3 @@ - - - diff --git a/dom/xslt/tests/mochitest/file_bug1135764.xsl b/dom/xslt/tests/mochitest/file_bug1135764.xsl index df83f06777..e739086cbe 100644 --- a/dom/xslt/tests/mochitest/file_bug1135764.xsl +++ b/dom/xslt/tests/mochitest/file_bug1135764.xsl @@ -17,22 +17,3 @@ - - - - - - - - - - - Some text - - - - - diff --git a/dom/xslt/tests/mochitest/test_bug1135764.html b/dom/xslt/tests/mochitest/test_bug1135764.html index b6dd6283e4..5a7410e37a 100644 --- a/dom/xslt/tests/mochitest/test_bug1135764.html +++ b/dom/xslt/tests/mochitest/test_bug1135764.html @@ -51,48 +51,3 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1135764 - - - - - - Test for Bug 1135764 - - - - - -Mozilla Bug 1135764 -

- -

- -
-
- - diff --git a/embedding/tests/unit/test_wwauthpromptfactory.js b/embedding/tests/unit/test_wwauthpromptfactory.js index aba2cf684f..358e5ca6da 100644 --- a/embedding/tests/unit/test_wwauthpromptfactory.js +++ b/embedding/tests/unit/test_wwauthpromptfactory.js @@ -1,5 +1,5 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; +var Cc = Components.classes; +var Ci = Components.interfaces; var authPromptRequestReceived; diff --git a/gfx/2d/DrawTargetSkia.cpp b/gfx/2d/DrawTargetSkia.cpp index bbda7f1d11..39290c4beb 100644 --- a/gfx/2d/DrawTargetSkia.cpp +++ b/gfx/2d/DrawTargetSkia.cpp @@ -161,7 +161,7 @@ DrawTargetSkia::LockBits(uint8_t** aData, IntSize* aSize, /* 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, nullptr)) { + if (!mCanvas->peekPixels(nullptr)) { return false; } @@ -210,12 +210,12 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0) SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); - SkShader* shader = SkGradientShader::CreateLinear(points, - &stops->mColors.front(), - &stops->mPositions.front(), - stops->mCount, - mode, 0, &mat); - SkSafeUnref(aPaint.setShader(shader)); + sk_sp shader = SkGradientShader::MakeLinear(points, + &stops->mColors.front(), + &stops->mPositions.front(), + stops->mCount, + mode, 0, &mat); + aPaint.setShader(shader); } else { aPaint.setColor(SK_ColorTRANSPARENT); } @@ -233,15 +233,15 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0) SkMatrix mat; GfxMatrixToSkiaMatrix(pat.mMatrix, mat); - SkShader* shader = SkGradientShader::CreateTwoPointConical(points[0], - SkFloatToScalar(pat.mRadius1), - points[1], - SkFloatToScalar(pat.mRadius2), - &stops->mColors.front(), - &stops->mPositions.front(), - stops->mCount, - mode, 0, &mat); - SkSafeUnref(aPaint.setShader(shader)); + sk_sp 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); } else { aPaint.setColor(SK_ColorTRANSPARENT); } @@ -263,8 +263,8 @@ SetPaintPattern(SkPaint& aPaint, const Pattern& aPattern, Float aAlpha = 1.0) SkShader::TileMode xTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::X_AXIS); SkShader::TileMode yTileMode = ExtendModeToTileMode(pat.mExtendMode, Axis::Y_AXIS); - SkShader* shader = SkShader::CreateBitmapShader(bitmap, xTileMode, yTileMode, &mat); - SkSafeUnref(aPaint.setShader(shader)); + sk_sp shader = SkShader::MakeBitmapShader(bitmap, xTileMode, yTileMode, &mat); + aPaint.setShader(shader); if (pat.mFilter == Filter::POINT) { aPaint.setFilterQuality(kNone_SkFilterQuality); } @@ -439,7 +439,7 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface, // to blur the image ourselves. SkPaint shadowPaint; - shadowPaint.setXfermode(paint.getXfermode()); + shadowPaint.setXfermodeMode(GfxOpToSkiaOp(aOperator)); IntPoint shadowDest = RoundedToInt(aDest + aOffset); @@ -461,12 +461,12 @@ DrawTargetSkia::DrawSurfaceWithShadow(SourceSurface *aSurface, mCanvas->drawBitmap(blurMask, shadowDest.x, shadowDest.y, &shadowPaint); } else { - SkAutoTUnref blurFilter(SkBlurImageFilter::Create(aSigma, aSigma)); - SkAutoTUnref colorFilter( - SkColorFilter::CreateModeFilter(ColorToSkColor(aColor, 1.0f), SkXfermode::kSrcIn_Mode)); + sk_sp blurFilter(SkBlurImageFilter::Make(aSigma, aSigma, nullptr)); + sk_sp colorFilter( + SkColorFilter::MakeModeFilter(ColorToSkColor(aColor, 1.0f), SkXfermode::kSrcIn_Mode)); - shadowPaint.setImageFilter(blurFilter.get()); - shadowPaint.setColorFilter(colorFilter.get()); + shadowPaint.setImageFilter(blurFilter); + shadowPaint.setColorFilter(colorFilter); mCanvas->drawBitmap(bitmap, shadowDest.x, shadowDest.y, &shadowPaint); } @@ -673,8 +673,8 @@ DrawTargetSkia::Mask(const Pattern &aSource, SkLayerRasterizer::Builder builder; builder.addLayer(maskPaint); - SkAutoTUnref raster(builder.detachRasterizer()); - paint.mPaint.setRasterizer(raster.get()); + sk_sp raster(builder.detach()); + paint.mPaint.setRasterizer(raster); mCanvas->drawPaint(paint.mPaint); } @@ -699,8 +699,8 @@ DrawTargetSkia::MaskSurface(const Pattern &aSource, paint.mPaint.getShader()) { SkMatrix transform; transform.setTranslate(PointToSkPoint(-aOffset)); - SkShader* matrixShader = paint.mPaint.getShader()->newWithLocalMatrix(transform); - SkSafeUnref(paint.mPaint.setShader(matrixShader)); + sk_sp matrixShader = paint.mPaint.getShader()->makeWithLocalMatrix(transform); + paint.mPaint.setShader(matrixShader); } mCanvas->drawBitmap(bitmap, aOffset.x, aOffset.y, &paint.mPaint); @@ -1023,10 +1023,10 @@ DrawTargetSkia::InitWithGrContext(GrContext* aGrContext, // Create a GPU rendertarget/texture using the supplied GrContext. // NewRenderTarget also implicitly clears the underlying texture on creation. - SkAutoTUnref gpuSurface( - SkSurface::NewRenderTarget(aGrContext, - SkSurface::Budgeted(aCached), - MakeSkiaImageInfo(aSize, aFormat))); + sk_sp gpuSurface = + SkSurface::MakeRenderTarget(aGrContext, + SkBudgeted(aCached), + MakeSkiaImageInfo(aSize, aFormat)); if (!gpuSurface) { return false; } @@ -1162,8 +1162,8 @@ public: : SkImageFilter(0, nullptr) {} - virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const override { + virtual bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const override { *result = src; offset->set(0, 0); return true; @@ -1173,11 +1173,11 @@ public: SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(CopyLayerImageFilter) }; -SkFlattenable* +sk_sp CopyLayerImageFilter::CreateProc(SkReadBuffer& buffer) { SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 0); - return new CopyLayerImageFilter; + return sk_make_sp(); } #ifndef SK_IGNORE_TO_STRING @@ -1250,11 +1250,11 @@ DrawTargetSkia::PopLayer() // 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()); - SkShader* shader = SkShader::CreateBitmapShader(layerBitmap, - SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode, - &layerMat); - SkSafeUnref(paint.setShader(shader)); + sk_sp shader = SkShader::MakeBitmapShader(layerBitmap, + SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, + &layerMat); + paint.setShader(shader); SkBitmap mask = GetBitmapForSurface(layer.mMask); if (mask.colorType() != kAlpha_8_SkColorType && diff --git a/gfx/2d/HelpersSkia.h b/gfx/2d/HelpersSkia.h index a0a6a1b675..1c36f0a9e6 100644 --- a/gfx/2d/HelpersSkia.h +++ b/gfx/2d/HelpersSkia.h @@ -176,10 +176,10 @@ StrokeOptionsToPaint(SkPaint& aPaint, const StrokeOptions &aOptions) pattern[i] = SkFloatToScalar(aOptions.mDashPattern[i % aOptions.mDashLength]); } - SkPathEffect* dash = SkDashPathEffect::Create(&pattern.front(), - dashCount, - SkFloatToScalar(aOptions.mDashOffset)); - SkSafeUnref(aPaint.setPathEffect(dash)); + sk_sp dash = SkDashPathEffect::Make(&pattern.front(), + dashCount, + SkFloatToScalar(aOptions.mDashOffset)); + aPaint.setPathEffect(dash); } aPaint.setStyle(SkPaint::kStroke_Style); diff --git a/gfx/gl/SkiaGLGlue.cpp b/gfx/gl/SkiaGLGlue.cpp index 8b9e787fb1..172c3500f1 100755 --- a/gfx/gl/SkiaGLGlue.cpp +++ b/gfx/gl/SkiaGLGlue.cpp @@ -24,318 +24,44 @@ using mozilla::gl::GLFeature; using mozilla::gl::SkiaGLGlue; using mozilla::gfx::DrawTarget; -static MOZ_THREAD_LOCAL(GLContext*) sGLContext; - -extern "C" { - -static void SetStaticGLContext(GLContext* context) +template +static inline GrGLFunction +WrapGL(RefPtr aContext, R (GLContext::*aFunc)(A...)) { - mozilla::DebugOnly success = sGLContext.init(); - MOZ_ASSERT(success); - - sGLContext.set(context); + return [aContext, aFunc] (A... args) -> R + { + aContext->MakeCurrent(); + return (aContext->*aFunc)(args...); + }; } -void EnsureGLContext(const GrGLInterface* i) +template +static inline GrGLFunction +WrapGL(RefPtr aContext, R (*aFunc)(GLContext*, A...)) { - const SkiaGLGlue* contextSkia = reinterpret_cast(i->fCallbackData); - MOZ_ASSERT(contextSkia); - GLContext* gl = contextSkia->GetGLContext(); - gl->MakeCurrent(); - SetStaticGLContext(gl); + return [aContext, aFunc] (A... args) -> R + { + aContext->MakeCurrent(); + return (*aFunc)(aContext, args...); + }; } // Core GL functions required by Ganesh -GrGLvoid glActiveTexture_mozilla(GrGLenum texture) -{ - return sGLContext.get()->fActiveTexture(texture); -} - -GrGLvoid glAttachShader_mozilla(GrGLuint program, GrGLuint shader) -{ - return sGLContext.get()->fAttachShader(program, shader); -} - -GrGLvoid glBindAttribLocation_mozilla(GrGLuint program, GrGLuint index, const GLchar* name) -{ - return sGLContext.get()->fBindAttribLocation(program, index, name); -} - -GrGLvoid glBindBuffer_mozilla(GrGLenum target, GrGLuint buffer) -{ - return sGLContext.get()->fBindBuffer(target, buffer); -} - -GrGLvoid glBindFramebuffer_mozilla(GrGLenum target, GrGLuint framebuffer) -{ - return sGLContext.get()->fBindFramebuffer(target, framebuffer); -} - -GrGLvoid glBindRenderbuffer_mozilla(GrGLenum target, GrGLuint renderbuffer) -{ - return sGLContext.get()->fBindRenderbuffer(target, renderbuffer); -} - -GrGLvoid glBindTexture_mozilla(GrGLenum target, GrGLuint texture) -{ - return sGLContext.get()->fBindTexture(target, texture); -} - -GrGLvoid glBlendColor_mozilla(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) -{ - return sGLContext.get()->fBlendColor(red, green, blue, alpha); -} - -GrGLvoid glBlendEquation_mozilla(GrGLenum mode) -{ - return sGLContext.get()->fBlendEquation(mode); -} - -GrGLvoid glBlendFunc_mozilla(GrGLenum sfactor, GrGLenum dfactor) -{ - return sGLContext.get()->fBlendFunc(sfactor, dfactor); -} - -GrGLvoid glBufferData_mozilla(GrGLenum target, GrGLsizeiptr size, const void* data, GrGLenum usage) -{ - return sGLContext.get()->fBufferData(target, size, data, usage); -} - -GrGLvoid glBufferSubData_mozilla(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const void* data) -{ - return sGLContext.get()->fBufferSubData(target, offset, size, data); -} - -GrGLenum glCheckFramebufferStatus_mozilla(GrGLenum target) -{ - return sGLContext.get()->fCheckFramebufferStatus(target); -} - -GrGLvoid glClear_mozilla(GrGLbitfield mask) -{ - return sGLContext.get()->fClear(mask); -} - -GrGLvoid glClearColor_mozilla(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha) -{ - return sGLContext.get()->fClearColor(red, green, blue, alpha); -} - -GrGLvoid glClearStencil_mozilla(GrGLint s) -{ - return sGLContext.get()->fClearStencil(s); -} - -GrGLvoid glColorMask_mozilla(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha) -{ - return sGLContext.get()->fColorMask(red, green, blue, alpha); -} - -GrGLvoid glCompileShader_mozilla(GrGLuint shader) -{ - return sGLContext.get()->fCompileShader(shader); -} - -GrGLvoid glCopyTexSubImage2D_mozilla(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, - GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) -{ - return sGLContext.get()->fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); -} - -GrGLuint glCreateProgram_mozilla(void) -{ - return sGLContext.get()->fCreateProgram(); -} - -GrGLuint glCreateShader_mozilla(GrGLenum type) -{ - return sGLContext.get()->fCreateShader(type); -} - -GrGLvoid glCullFace_mozilla(GrGLenum mode) -{ - return sGLContext.get()->fCullFace(mode); -} - -GrGLvoid glDeleteBuffers_mozilla(GrGLsizei n, const GrGLuint* buffers) -{ - return sGLContext.get()->fDeleteBuffers(n, buffers); -} - -GrGLvoid glDeleteFramebuffers_mozilla(GrGLsizei n, const GrGLuint* framebuffers) -{ - return sGLContext.get()->fDeleteFramebuffers(n, framebuffers); -} - -GrGLvoid glDeleteProgram_mozilla(GrGLuint program) -{ - return sGLContext.get()->fDeleteProgram(program); -} - -GrGLvoid glDeleteRenderbuffers_mozilla(GrGLsizei n, const GrGLuint* renderbuffers) -{ - return sGLContext.get()->fDeleteRenderbuffers(n, renderbuffers); -} - -GrGLvoid glDeleteShader_mozilla(GrGLuint shader) -{ - return sGLContext.get()->fDeleteShader(shader); -} - -GrGLvoid glDeleteTextures_mozilla(GrGLsizei n, const GrGLuint* textures) -{ - return sGLContext.get()->fDeleteTextures(n, textures); -} - -GrGLvoid glDepthMask_mozilla(GrGLboolean flag) -{ - return sGLContext.get()->fDepthMask(flag); -} - -GrGLvoid glDisable_mozilla(GrGLenum cap) -{ - return sGLContext.get()->fDisable(cap); -} - -GrGLvoid glDisableVertexAttribArray_mozilla(GrGLuint index) -{ - return sGLContext.get()->fDisableVertexAttribArray(index); -} - -GrGLvoid glDrawArrays_mozilla(GrGLenum mode, GrGLint first, GrGLsizei count) -{ - return sGLContext.get()->fDrawArrays(mode, first, count); -} - -GrGLvoid glDrawElements_mozilla(GrGLenum mode, GrGLsizei count, GrGLenum type, const void* indices) -{ - return sGLContext.get()->fDrawElements(mode, count, type, indices); -} - -GrGLvoid glEnable_mozilla(GrGLenum cap) -{ - return sGLContext.get()->fEnable(cap); -} - -GrGLvoid glEnableVertexAttribArray_mozilla(GrGLuint index) -{ - return sGLContext.get()->fEnableVertexAttribArray(index); -} - -GrGLvoid glFinish_mozilla() -{ - return sGLContext.get()->fFinish(); -} - -GrGLvoid glFlush_mozilla() -{ - return sGLContext.get()->fFlush(); -} - -GrGLvoid glFramebufferRenderbuffer_mozilla(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer) -{ - return sGLContext.get()->fFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); -} - -GrGLvoid glFramebufferTexture2D_mozilla(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level) -{ - return sGLContext.get()->fFramebufferTexture2D(target, attachment, textarget, texture, level); -} - -GrGLvoid glFrontFace_mozilla(GrGLenum mode) -{ - return sGLContext.get()->fFrontFace(mode); -} - -GrGLvoid glGenBuffers_mozilla(GrGLsizei n, GrGLuint* buffers) -{ - return sGLContext.get()->fGenBuffers(n, buffers); -} - -GrGLvoid glGenFramebuffers_mozilla(GrGLsizei n, GrGLuint* framebuffers) -{ - return sGLContext.get()->fGenFramebuffers(n, framebuffers); -} - -GrGLvoid glGenRenderbuffers_mozilla(GrGLsizei n, GrGLuint* renderbuffers) -{ - return sGLContext.get()->fGenRenderbuffers(n, renderbuffers); -} - -GrGLvoid glGenTextures_mozilla(GrGLsizei n, GrGLuint* textures) -{ - return sGLContext.get()->fGenTextures(n, textures); -} - -GrGLvoid glGenerateMipmap_mozilla(GrGLenum target) -{ - return sGLContext.get()->fGenerateMipmap(target); -} - -GrGLvoid glGetBufferParameteriv_mozilla(GrGLenum target, GrGLenum pname, GrGLint* params) -{ - return sGLContext.get()->fGetBufferParameteriv(target, pname, params); -} - -GrGLvoid glGetFramebufferAttachmentParameteriv_mozilla(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params) -{ - return sGLContext.get()->fGetFramebufferAttachmentParameteriv(target, attachment, pname, params); -} - -GrGLenum glGetError_mozilla() -{ - return sGLContext.get()->fGetError(); -} - -GrGLvoid glGetIntegerv_mozilla(GrGLenum pname, GrGLint* params) -{ - return sGLContext.get()->fGetIntegerv(pname, params); -} - -GrGLvoid glGetProgramInfoLog_mozilla(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, char* infolog) -{ - return sGLContext.get()->fGetProgramInfoLog(program, bufsize, length, infolog); -} - -GrGLvoid glGetProgramiv_mozilla(GrGLuint program, GrGLenum pname, GrGLint* params) -{ - return sGLContext.get()->fGetProgramiv(program, pname, params); -} - -GrGLvoid glGetRenderbufferParameteriv_mozilla(GrGLenum target, GrGLenum pname, GrGLint* params) -{ - return sGLContext.get()->fGetRenderbufferParameteriv(target, pname, params); -} - -GrGLvoid glGetShaderInfoLog_mozilla(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, char* infolog) -{ - return sGLContext.get()->fGetShaderInfoLog(shader, bufsize, length, infolog); -} - -GrGLvoid glGetShaderiv_mozilla(GrGLuint shader, GrGLenum pname, GrGLint* params) -{ - return sGLContext.get()->fGetShaderiv(shader, pname, params); -} - -GrGLvoid glGetShaderPrecisionFormat_mozilla(GrGLenum shadertype, GrGLenum precisiontype, GLint *range, GLint *precision) -{ - return sGLContext.get()->fGetShaderPrecisionFormat(shadertype, precisiontype, range, precision); -} - -const GLubyte* glGetString_mozilla(GrGLenum name) +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 (name == LOCAL_GL_VERSION) { - if (sGLContext.get()->IsGLES()) { + if (aName == LOCAL_GL_VERSION) { + if (aContext->IsGLES()) { return reinterpret_cast("OpenGL ES 2.0"); } else { return reinterpret_cast("2.0"); } - } else if (name == LOCAL_GL_EXTENSIONS) { + } 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; @@ -344,52 +70,52 @@ const GLubyte* glGetString_mozilla(GrGLenum name) if (!extensionsStringBuilt) { extensionsString[0] = '\0'; - if (sGLContext.get()->IsGLES()) { + if (aContext->IsGLES()) { // OES is only applicable to GLES2 - if (sGLContext.get()->IsExtensionSupported(GLContext::OES_packed_depth_stencil)) { + if (aContext->IsExtensionSupported(GLContext::OES_packed_depth_stencil)) { strcat(extensionsString, "GL_OES_packed_depth_stencil "); } - if (sGLContext.get()->IsExtensionSupported(GLContext::OES_rgb8_rgba8)) { + if (aContext->IsExtensionSupported(GLContext::OES_rgb8_rgba8)) { strcat(extensionsString, "GL_OES_rgb8_rgba8 "); } - if (sGLContext.get()->IsExtensionSupported(GLContext::OES_texture_npot)) { + if (aContext->IsExtensionSupported(GLContext::OES_texture_npot)) { strcat(extensionsString, "GL_OES_texture_npot "); } - if (sGLContext.get()->IsExtensionSupported(GLContext::OES_vertex_array_object)) { + if (aContext->IsExtensionSupported(GLContext::OES_vertex_array_object)) { strcat(extensionsString, "GL_OES_vertex_array_object "); } - if (sGLContext.get()->IsSupported(GLFeature::standard_derivatives)) { + if (aContext->IsSupported(GLFeature::standard_derivatives)) { strcat(extensionsString, "GL_OES_standard_derivatives "); } } else { - if (sGLContext.get()->IsSupported(GLFeature::framebuffer_object)) { + if (aContext->IsSupported(GLFeature::framebuffer_object)) { strcat(extensionsString, "GL_ARB_framebuffer_object "); - } else if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_framebuffer_object)) { + } else if (aContext->IsExtensionSupported(GLContext::EXT_framebuffer_object)) { strcat(extensionsString, "GL_EXT_framebuffer_object "); } - if (sGLContext.get()->IsSupported(GLFeature::texture_rg)) { + if (aContext->IsSupported(GLFeature::texture_rg)) { strcat(extensionsString, "GL_ARB_texture_rg "); } } - if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_texture_format_BGRA8888)) { + if (aContext->IsExtensionSupported(GLContext::EXT_texture_format_BGRA8888)) { strcat(extensionsString, "GL_EXT_texture_format_BGRA8888 "); } - if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_packed_depth_stencil)) { + if (aContext->IsExtensionSupported(GLContext::EXT_packed_depth_stencil)) { strcat(extensionsString, "GL_EXT_packed_depth_stencil "); } - if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_bgra)) { + if (aContext->IsExtensionSupported(GLContext::EXT_bgra)) { strcat(extensionsString, "GL_EXT_bgra "); } - if (sGLContext.get()->IsExtensionSupported(GLContext::EXT_read_format_bgra)) { + if (aContext->IsExtensionSupported(GLContext::EXT_read_format_bgra)) { strcat(extensionsString, "GL_EXT_read_format_bgra "); } @@ -401,377 +127,20 @@ const GLubyte* glGetString_mozilla(GrGLenum name) return reinterpret_cast(extensionsString); - } else if (name == LOCAL_GL_SHADING_LANGUAGE_VERSION) { - if (sGLContext.get()->IsGLES()) { + } else if (aName == LOCAL_GL_SHADING_LANGUAGE_VERSION) { + if (aContext->IsGLES()) { return reinterpret_cast("OpenGL ES GLSL ES 1.0"); } else { return reinterpret_cast("1.10"); } } - return sGLContext.get()->fGetString(name); + return aContext->fGetString(aName); } -GrGLint glGetUniformLocation_mozilla(GrGLuint program, const char* name) -{ - return sGLContext.get()->fGetUniformLocation(program, name); -} - -GrGLvoid glLineWidth_mozilla(GrGLfloat width) -{ - return sGLContext.get()->fLineWidth(width); -} - -GrGLvoid glLinkProgram_mozilla(GrGLuint program) -{ - return sGLContext.get()->fLinkProgram(program); -} - -GrGLvoid glPixelStorei_mozilla(GrGLenum pname, GrGLint param) -{ - return sGLContext.get()->fPixelStorei(pname, param); -} - -GrGLvoid glReadPixels_mozilla(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, - GrGLenum format, GrGLenum type, void* pixels) -{ - return sGLContext.get()->fReadPixels(x, y, width, height, - format, type, pixels); -} - -GrGLvoid glRenderbufferStorage_mozilla(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) -{ - return sGLContext.get()->fRenderbufferStorage(target, internalformat, width, height); -} - -GrGLvoid glScissor_mozilla(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) -{ - return sGLContext.get()->fScissor(x, y, width, height); -} - -GrGLvoid glShaderSource_mozilla(GrGLuint shader, GrGLsizei count, const char** str, const GrGLint* length) -{ - return sGLContext.get()->fShaderSource(shader, count, str, length); -} - -GrGLvoid glStencilFunc_mozilla(GrGLenum func, GrGLint ref, GrGLuint mask) -{ - return sGLContext.get()->fStencilFunc(func, ref, mask); -} - -GrGLvoid glStencilMask_mozilla(GrGLuint mask) -{ - return sGLContext.get()->fStencilMask(mask); -} - -GrGLvoid glStencilOp_mozilla(GrGLenum fail, GrGLenum zfail, GrGLenum zpass) -{ - return sGLContext.get()->fStencilOp(fail, zfail, zpass); -} - -GrGLvoid glTexImage2D_mozilla(GrGLenum target, GrGLint level, GrGLint internalformat, - GrGLsizei width, GrGLsizei height, GrGLint border, - GrGLenum format, GrGLenum type, const void* pixels) -{ - return sGLContext.get()->fTexImage2D(target, level, internalformat, - width, height, border, - format, type, pixels); -} - -GrGLvoid glTexParameteri_mozilla(GrGLenum target, GrGLenum pname, GrGLint param) -{ - return sGLContext.get()->fTexParameteri(target, pname, param); -} - -GrGLvoid glTexParameteriv_mozilla(GrGLenum target, GrGLenum pname, const GrGLint* params) -{ - return sGLContext.get()->fTexParameteriv(target, pname, params); -} - -GrGLvoid glTexSubImage2D_mozilla(GrGLenum target, GrGLint level, - GrGLint xoffset, GrGLint yoffset, - GrGLsizei width, GrGLsizei height, - GrGLenum format, GrGLenum type, const void* pixels) -{ - return sGLContext.get()->fTexSubImage2D(target, level, - xoffset, yoffset, - width, height, - format, type, pixels); -} - -GrGLvoid glUniform1f_mozilla(GrGLint location, GrGLfloat v) -{ - return sGLContext.get()->fUniform1f(location, v); -} - -GrGLvoid glUniform1i_mozilla(GrGLint location, GrGLint v) -{ - return sGLContext.get()->fUniform1i(location, v); -} - -GrGLvoid glUniform1fv_mozilla(GrGLint location, GrGLsizei count, const GrGLfloat* v) -{ - return sGLContext.get()->fUniform1fv(location, count, v); -} - -GrGLvoid glUniform1iv_mozilla(GrGLint location, GrGLsizei count, const GrGLint* v) -{ - return sGLContext.get()->fUniform1iv(location, count, v); -} - -GrGLvoid glUniform2f_mozilla(GrGLint location, GrGLfloat v0, GrGLfloat v1) -{ - return sGLContext.get()->fUniform2f(location, v0, v1); -} - -GrGLvoid glUniform2i_mozilla(GrGLint location, GrGLint v0, GrGLint v1) -{ - return sGLContext.get()->fUniform2i(location, v0, v1); -} - -GrGLvoid glUniform2fv_mozilla(GrGLint location, GrGLsizei count, const GrGLfloat* v) -{ - return sGLContext.get()->fUniform2fv(location, count, v); -} - -GrGLvoid glUniform2iv_mozilla(GrGLint location, GrGLsizei count, const GrGLint* v) -{ - return sGLContext.get()->fUniform2iv(location, count, v); -} - -GrGLvoid glUniform3f_mozilla(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2) -{ - return sGLContext.get()->fUniform3f(location, v0, v1, v2); -} - -GrGLvoid glUniform3i_mozilla(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2) -{ - return sGLContext.get()->fUniform3i(location, v0, v1, v2); -} - -GrGLvoid glUniform3fv_mozilla(GrGLint location, GrGLsizei count, const GrGLfloat* v) -{ - return sGLContext.get()->fUniform3fv(location, count, v); -} - -GrGLvoid glUniform3iv_mozilla(GrGLint location, GrGLsizei count, const GrGLint* v) -{ - return sGLContext.get()->fUniform3iv(location, count, v); -} - -GrGLvoid glUniform4f_mozilla(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2, GrGLfloat v3) -{ - return sGLContext.get()->fUniform4f(location, v0, v1, v2, v3); -} - -GrGLvoid glUniform4i_mozilla(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3) -{ - return sGLContext.get()->fUniform4i(location, v0, v1, v2, v3); -} - -GrGLvoid glUniform4fv_mozilla(GrGLint location, GrGLsizei count, const GrGLfloat* v) -{ - return sGLContext.get()->fUniform4fv(location, count, v); -} - -GrGLvoid glUniform4iv_mozilla(GrGLint location, GrGLsizei count, const GrGLint* v) -{ - return sGLContext.get()->fUniform4iv(location, count, v); -} - -GrGLvoid glUniformMatrix2fv_mozilla(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) -{ - return sGLContext.get()->fUniformMatrix2fv(location, count, transpose, value); -} - -GrGLvoid glUniformMatrix3fv_mozilla(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) -{ - return sGLContext.get()->fUniformMatrix3fv(location, count, transpose, value); -} - -GrGLvoid glUniformMatrix4fv_mozilla(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value) -{ - return sGLContext.get()->fUniformMatrix4fv(location, count, transpose, value); -} - -GrGLvoid glUseProgram_mozilla(GrGLuint program) -{ - return sGLContext.get()->fUseProgram(program); -} - -GrGLvoid glVertexAttrib1f_mozilla(GrGLuint index, GrGLfloat value) -{ - return sGLContext.get()->fVertexAttrib1f(index, value); -} - -GrGLvoid glVertexAttrib2fv_mozilla(GrGLuint index, const GrGLfloat* values) -{ - return sGLContext.get()->fVertexAttrib2fv(index, values); -} - -GrGLvoid glVertexAttrib3fv_mozilla(GrGLuint index, const GrGLfloat* values) -{ - return sGLContext.get()->fVertexAttrib3fv(index, values); -} - -GrGLvoid glVertexAttrib4fv_mozilla(GrGLuint index, const GrGLfloat* values) -{ - return sGLContext.get()->fVertexAttrib4fv(index, values); -} - -GrGLvoid glVertexAttribPointer_mozilla(GrGLuint index, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const void* ptr) -{ - return sGLContext.get()->fVertexAttribPointer(index, size, type, normalized, stride, ptr); -} - -GrGLvoid glViewport_mozilla(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) -{ - return sGLContext.get()->fViewport(x, y, width, height); -} - -// Required if the bindings are GLES2 or desktop OpenGL 2.0 - -GrGLvoid glStencilFuncSeparate_mozilla(GrGLenum frontfunc, GrGLenum backfunc, GrGLint ref, GrGLuint mask) -{ - return sGLContext.get()->fStencilFuncSeparate(frontfunc, backfunc, ref, mask); -} - -GrGLvoid glStencilMaskSeparate_mozilla(GrGLenum face, GrGLuint mask) -{ - return sGLContext.get()->fStencilMaskSeparate(face, mask); -} - -GrGLvoid glStencilOpSeparate_mozilla(GrGLenum face, GrGLenum sfail, GrGLenum dpfail, GrGLenum dppass) -{ - return sGLContext.get()->fStencilOpSeparate(face, sfail, dpfail, dppass); -} - -// Not in GLES2 - -GrGLvoid glGetTexLevelParameteriv_mozilla(GrGLenum target, GrGLint level, GrGLenum pname, GrGLint *params) -{ - return sGLContext.get()->fGetTexLevelParameteriv(target, level, pname, params); -} - -GrGLvoid glDrawBuffer_mozilla(GrGLenum mode) -{ - return sGLContext.get()->fDrawBuffer(mode); -} - -GrGLvoid glReadBuffer_mozilla(GrGLenum mode) -{ - return sGLContext.get()->fReadBuffer(mode); -} - -// Desktop OpenGL version >= 1.5 - -GrGLvoid glGenQueries_mozilla(GrGLsizei n, GrGLuint* ids) -{ - return sGLContext.get()->fGenQueries(n, ids); -} - -GrGLvoid glDeleteQueries_mozilla(GrGLsizei n, const GrGLuint* ids) -{ - return sGLContext.get()->fDeleteQueries(n, ids); -} - -GrGLvoid glBeginQuery_mozilla(GrGLenum target, GrGLuint id) -{ - return sGLContext.get()->fBeginQuery(target, id); -} - -GrGLvoid glEndQuery_mozilla(GrGLenum target) -{ - return sGLContext.get()->fEndQuery(target); -} - -GrGLvoid glGetQueryiv_mozilla(GrGLenum target, GrGLenum pname, GrGLint* params) -{ - return sGLContext.get()->fGetQueryiv(target, pname, params); -} - -GrGLvoid glGetQueryObjectiv_mozilla(GrGLuint id, GrGLenum pname, GrGLint* params) -{ - return sGLContext.get()->fGetQueryObjectiv(id, pname, params); -} - -GrGLvoid glGetQueryObjectuiv_mozilla(GrGLuint id, GrGLenum pname, GrGLuint* params) -{ - return sGLContext.get()->fGetQueryObjectuiv(id, pname, params); -} - -// Desktop OpenGL version >= 2.0 - -GrGLvoid glDrawBuffers_mozilla(GrGLsizei n, const GrGLenum* bufs) -{ - return sGLContext.get()->fDrawBuffers(n, bufs); -} - -// GLContext supports glMapBuffer on everything (GL_OES_mapbuffer) - -GrGLvoid* glMapBuffer_mozilla(GrGLenum target, GrGLenum access) -{ - return sGLContext.get()->fMapBuffer(target, access); -} - -GrGLboolean glUnmapBuffer_mozilla(GrGLenum target) -{ - return sGLContext.get()->fUnmapBuffer(target); -} - -// GLContext supports glCompressedTexImage2D (GL_ARB_texture_compression) - -GrGLvoid glCompressedTexImage2D_mozilla(GrGLenum target, GrGLint level, GrGLenum internalformat, - GrGLsizei width, GrGLsizei height, GrGLint border, - GrGLsizei imageSize, const GrGLvoid* pixels) -{ - return sGLContext.get()->fCompressedTexImage2D(target, level, internalformat, - width, height, border, - imageSize, pixels); -} - -// GLContext supports glBlitFramebuffer/glRenderbufferStorageMultisample (GL_ARB_framebuffer_object) - -GrGLvoid glRenderbufferStorageMultisample_mozilla(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, - GrGLsizei width, GrGLsizei height) -{ - return sGLContext.get()->fRenderbufferStorageMultisample(target, samples, internalformat, - width, height); -} - -GrGLvoid glBlitFramebuffer_mozilla(GrGLint srcX0, GrGLint srcY0, - GrGLint srcX1, GrGLint srcY1, - GrGLint dstX0, GrGLint dstY0, - GrGLint dstX1, GrGLint dstY1, - GrGLbitfield mask, GrGLenum filter) { - return sGLContext.get()->fBlitFramebuffer(srcX0, srcY0, - srcX1, srcY1, - dstX0, dstY0, - dstX1, dstY1, - mask, filter); -} - -GrGLvoid glBindVertexArray_mozilla(GrGLuint array) { - return sGLContext.get()->fBindVertexArray(array); -} - -GrGLvoid glDeleteVertexArrays_mozilla(GrGLsizei n, const GrGLuint *arrays) { - return sGLContext.get()->fDeleteVertexArrays(n, arrays); -} - -GrGLvoid glGenVertexArrays_mozilla(GrGLsizei n, GrGLuint *arrays) { - return sGLContext.get()->fGenVertexArrays(n, arrays); -} - -} // extern "C" - static GrGLInterface* CreateGrGLInterfaceFromGLContext(GLContext* context) { - SetStaticGLContext(context); - GrGLInterface* i = new GrGLInterface(); - i->fCallback = EnsureGLContext; - i->fCallbackData = 0; // must be later initialized to be a valid DrawTargetSkia* pointer context->MakeCurrent(); @@ -782,8 +151,11 @@ static GrGLInterface* CreateGrGLInterfaceFromGLContext(GLContext* context) i->fStandard = kGL_GrGLStandard; } + GrGLFunction getString = WrapGL(context, &glGetString_mozilla); + GrGLFunction getIntegerv = WrapGL(context, &GLContext::fGetIntegerv); + GrGLExtensions extensions; - if (!extensions.init(i->fStandard, glGetString_mozilla, NULL, glGetIntegerv_mozilla)) { + if (!extensions.init(i->fStandard, getString, nullptr, getIntegerv)) { delete i; return nullptr; } @@ -791,141 +163,141 @@ static GrGLInterface* CreateGrGLInterfaceFromGLContext(GLContext* context) i->fExtensions.swap(&extensions); // Core GL functions required by Ganesh - i->fFunctions.fActiveTexture = glActiveTexture_mozilla; - i->fFunctions.fAttachShader = glAttachShader_mozilla; - i->fFunctions.fBindAttribLocation = glBindAttribLocation_mozilla; - i->fFunctions.fBindBuffer = glBindBuffer_mozilla; - i->fFunctions.fBindFramebuffer = glBindFramebuffer_mozilla; - i->fFunctions.fBindRenderbuffer = glBindRenderbuffer_mozilla; - i->fFunctions.fBindTexture = glBindTexture_mozilla; - i->fFunctions.fBlendFunc = glBlendFunc_mozilla; - i->fFunctions.fBlendColor = glBlendColor_mozilla; - i->fFunctions.fBlendEquation = glBlendEquation_mozilla; - i->fFunctions.fBufferData = glBufferData_mozilla; - i->fFunctions.fBufferSubData = glBufferSubData_mozilla; - i->fFunctions.fCheckFramebufferStatus = glCheckFramebufferStatus_mozilla; - i->fFunctions.fClear = glClear_mozilla; - i->fFunctions.fClearColor = glClearColor_mozilla; - i->fFunctions.fClearStencil = glClearStencil_mozilla; - i->fFunctions.fColorMask = glColorMask_mozilla; - i->fFunctions.fCompileShader = glCompileShader_mozilla; - i->fFunctions.fCopyTexSubImage2D = glCopyTexSubImage2D_mozilla; - i->fFunctions.fCreateProgram = glCreateProgram_mozilla; - i->fFunctions.fCreateShader = glCreateShader_mozilla; - i->fFunctions.fCullFace = glCullFace_mozilla; - i->fFunctions.fDeleteBuffers = glDeleteBuffers_mozilla; - i->fFunctions.fDeleteFramebuffers = glDeleteFramebuffers_mozilla; - i->fFunctions.fDeleteProgram = glDeleteProgram_mozilla; - i->fFunctions.fDeleteRenderbuffers = glDeleteRenderbuffers_mozilla; - i->fFunctions.fDeleteShader = glDeleteShader_mozilla; - i->fFunctions.fDeleteTextures = glDeleteTextures_mozilla; - i->fFunctions.fDepthMask = glDepthMask_mozilla; - i->fFunctions.fDisable = glDisable_mozilla; - i->fFunctions.fDisableVertexAttribArray = glDisableVertexAttribArray_mozilla; - i->fFunctions.fDrawArrays = glDrawArrays_mozilla; - i->fFunctions.fDrawElements = glDrawElements_mozilla; - i->fFunctions.fEnable = glEnable_mozilla; - i->fFunctions.fEnableVertexAttribArray = glEnableVertexAttribArray_mozilla; - i->fFunctions.fFinish = glFinish_mozilla; - i->fFunctions.fFlush = glFlush_mozilla; - i->fFunctions.fFramebufferRenderbuffer = glFramebufferRenderbuffer_mozilla; - i->fFunctions.fFramebufferTexture2D = glFramebufferTexture2D_mozilla; - i->fFunctions.fFrontFace = glFrontFace_mozilla; - i->fFunctions.fGenBuffers = glGenBuffers_mozilla; - i->fFunctions.fGenFramebuffers = glGenFramebuffers_mozilla; - i->fFunctions.fGenRenderbuffers = glGenRenderbuffers_mozilla; - i->fFunctions.fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv_mozilla; - i->fFunctions.fGenTextures = glGenTextures_mozilla; - i->fFunctions.fGenerateMipmap = glGenerateMipmap_mozilla; - i->fFunctions.fGetBufferParameteriv = glGetBufferParameteriv_mozilla; - i->fFunctions.fGetError = glGetError_mozilla; - i->fFunctions.fGetIntegerv = glGetIntegerv_mozilla; - i->fFunctions.fGetProgramInfoLog = glGetProgramInfoLog_mozilla; - i->fFunctions.fGetProgramiv = glGetProgramiv_mozilla; - i->fFunctions.fGetRenderbufferParameteriv = glGetRenderbufferParameteriv_mozilla; - i->fFunctions.fGetShaderInfoLog = glGetShaderInfoLog_mozilla; - i->fFunctions.fGetShaderiv = glGetShaderiv_mozilla; - i->fFunctions.fGetShaderPrecisionFormat = glGetShaderPrecisionFormat_mozilla; - i->fFunctions.fGetString = glGetString_mozilla; - i->fFunctions.fGetUniformLocation = glGetUniformLocation_mozilla; - i->fFunctions.fLineWidth = glLineWidth_mozilla; - i->fFunctions.fLinkProgram = glLinkProgram_mozilla; - i->fFunctions.fPixelStorei = glPixelStorei_mozilla; - i->fFunctions.fReadPixels = glReadPixels_mozilla; - i->fFunctions.fRenderbufferStorage = glRenderbufferStorage_mozilla; - i->fFunctions.fScissor = glScissor_mozilla; - i->fFunctions.fShaderSource = glShaderSource_mozilla; - i->fFunctions.fStencilFunc = glStencilFunc_mozilla; - i->fFunctions.fStencilMask = glStencilMask_mozilla; - i->fFunctions.fStencilOp = glStencilOp_mozilla; - i->fFunctions.fTexImage2D = glTexImage2D_mozilla; - i->fFunctions.fTexParameteri = glTexParameteri_mozilla; - i->fFunctions.fTexParameteriv = glTexParameteriv_mozilla; - i->fFunctions.fTexSubImage2D = glTexSubImage2D_mozilla; - i->fFunctions.fUniform1f = glUniform1f_mozilla; - i->fFunctions.fUniform1i = glUniform1i_mozilla; - i->fFunctions.fUniform1fv = glUniform1fv_mozilla; - i->fFunctions.fUniform1iv = glUniform1iv_mozilla; - i->fFunctions.fUniform2f = glUniform2f_mozilla; - i->fFunctions.fUniform2i = glUniform2i_mozilla; - i->fFunctions.fUniform2fv = glUniform2fv_mozilla; - i->fFunctions.fUniform2iv = glUniform2iv_mozilla; - i->fFunctions.fUniform3f = glUniform3f_mozilla; - i->fFunctions.fUniform3i = glUniform3i_mozilla; - i->fFunctions.fUniform3fv = glUniform3fv_mozilla; - i->fFunctions.fUniform3iv = glUniform3iv_mozilla; - i->fFunctions.fUniform4f = glUniform4f_mozilla; - i->fFunctions.fUniform4i = glUniform4i_mozilla; - i->fFunctions.fUniform4fv = glUniform4fv_mozilla; - i->fFunctions.fUniform4iv = glUniform4iv_mozilla; - i->fFunctions.fUniformMatrix2fv = glUniformMatrix2fv_mozilla; - i->fFunctions.fUniformMatrix3fv = glUniformMatrix3fv_mozilla; - i->fFunctions.fUniformMatrix4fv = glUniformMatrix4fv_mozilla; - i->fFunctions.fUseProgram = glUseProgram_mozilla; - i->fFunctions.fVertexAttrib1f = glVertexAttrib1f_mozilla; - i->fFunctions.fVertexAttrib2fv = glVertexAttrib2fv_mozilla; - i->fFunctions.fVertexAttrib3fv = glVertexAttrib3fv_mozilla; - i->fFunctions.fVertexAttrib4fv = glVertexAttrib4fv_mozilla; - i->fFunctions.fVertexAttribPointer = glVertexAttribPointer_mozilla; - i->fFunctions.fViewport = glViewport_mozilla; + 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.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 = glStencilFuncSeparate_mozilla; - i->fFunctions.fStencilMaskSeparate = glStencilMaskSeparate_mozilla; - i->fFunctions.fStencilOpSeparate = glStencilOpSeparate_mozilla; + 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 = glMapBuffer_mozilla; - i->fFunctions.fUnmapBuffer = glUnmapBuffer_mozilla; + i->fFunctions.fMapBuffer = WrapGL(context, &GLContext::fMapBuffer); + i->fFunctions.fUnmapBuffer = WrapGL(context, &GLContext::fUnmapBuffer); // GLContext supports glRenderbufferStorageMultisample/glBlitFramebuffer - i->fFunctions.fRenderbufferStorageMultisample = glRenderbufferStorageMultisample_mozilla; - i->fFunctions.fBlitFramebuffer = glBlitFramebuffer_mozilla; + i->fFunctions.fRenderbufferStorageMultisample = WrapGL(context, &GLContext::fRenderbufferStorageMultisample); + i->fFunctions.fBlitFramebuffer = WrapGL(context, &GLContext::fBlitFramebuffer); // GLContext supports glCompressedTexImage2D - i->fFunctions.fCompressedTexImage2D = glCompressedTexImage2D_mozilla; + i->fFunctions.fCompressedTexImage2D = WrapGL(context, &GLContext::fCompressedTexImage2D); // GL_OES_vertex_array_object - i->fFunctions.fBindVertexArray = glBindVertexArray_mozilla; - i->fFunctions.fDeleteVertexArrays = glDeleteVertexArrays_mozilla; - i->fFunctions.fGenVertexArrays = glGenVertexArrays_mozilla; + 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 = glGetTexLevelParameteriv_mozilla; - i->fFunctions.fDrawBuffer = glDrawBuffer_mozilla; - i->fFunctions.fReadBuffer = glReadBuffer_mozilla; + 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 = glGenQueries_mozilla; - i->fFunctions.fDeleteQueries = glDeleteQueries_mozilla; - i->fFunctions.fBeginQuery = glBeginQuery_mozilla; - i->fFunctions.fEndQuery = glEndQuery_mozilla; - i->fFunctions.fGetQueryiv = glGetQueryiv_mozilla; - i->fFunctions.fGetQueryObjectiv = glGetQueryObjectiv_mozilla; - i->fFunctions.fGetQueryObjectuiv = glGetQueryObjectuiv_mozilla; + 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 = glDrawBuffers_mozilla; + i->fFunctions.fDrawBuffers = WrapGL(context, &GLContext::fDrawBuffers); return i; } @@ -934,7 +306,6 @@ SkiaGLGlue::SkiaGLGlue(GLContext* context) : mGLContext(context) { mGrGLInterface.adopt(CreateGrGLInterfaceFromGLContext(mGLContext)); - mGrGLInterface->fCallbackData = reinterpret_cast(this); mGrContext.adopt(GrContext::Create(kOpenGL_GrBackend, (GrBackendContext)mGrGLInterface.get())); } @@ -946,6 +317,10 @@ SkiaGLGlue::~SkiaGLGlue() * through it, uses mGLContext */ mGrContext = nullptr; - mGrGLInterface = 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/gfx/skia/generate_mozbuild.py b/gfx/skia/generate_mozbuild.py index 054ad1e711..8693bf762f 100755 --- a/gfx/skia/generate_mozbuild.py +++ b/gfx/skia/generate_mozbuild.py @@ -85,29 +85,23 @@ if CONFIG['_MSC_VER']: SOURCES['skia/src/opts/SkBitmapProcState_opts_SSE2.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=20'] SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=31'] SOURCES['skia/src/opts/SkBlitRow_opts_SSE2.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=20'] - SOURCES['skia/src/opts/SkBlitRow_opts_SSE4.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=41'] SOURCES['skia/src/opts/SkOpts_sse2.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=20'] SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=31'] SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=41'] - SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['/arch:AVX -DSK_CPU_SSE_LEVEL=51'] if CONFIG['INTEL_ARCHITECTURE'] and CONFIG['GNU_CC']: SOURCES['skia/src/opts/SkBitmapFilter_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] SOURCES['skia/src/opts/SkBitmapProcState_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3'] SOURCES['skia/src/opts/SkBlitRow_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] - SOURCES['skia/src/opts/SkBlitRow_opts_SSE4.cpp'].flags += ['-msse4.1'] SOURCES['skia/src/opts/SkOpts_sse2.cpp'].flags += CONFIG['SSE2_FLAGS'] SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['-mssse3'] SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1'] - SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx'] elif CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC'] and CONFIG['BUILD_ARM_NEON']: DEFINES['SK_ARM_HAS_OPTIONAL_NEON'] = 1 elif CONFIG['CLANG_CL']: SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3'] - SOURCES['skia/src/opts/SkBlitRow_opts_SSE4.cpp'].flags += ['-msse4.1'] SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['-mssse3'] SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1'] - SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx'] if CONFIG['GNU_CXX'] and CONFIG['CPU_ARCH'] == 'arm': SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer'] @@ -177,11 +171,9 @@ def generate_platform_sources(): def generate_separated_sources(platform_sources): blacklist = [ - 'ChromeUtils', - 'SkJpeg', + 'experimental', 'SkXML', 'GrGLCreateNativeInterface', - 'SkCreatePlatformGLContext', 'fontconfig', 'SkThreadUtils_pthread_', 'SkFontConfig', @@ -189,10 +181,9 @@ def generate_separated_sources(platform_sources): 'SkFontMgr_custom', 'SkFontHost_FreeType.cpp', 'SkForceLinking', - 'SkMovie', - 'SkImageDecoder', - 'SkImageEncoder', - 'SkBitmapHasher', + 'Movie', + 'ImageEncoder', + 'skia/src/ports/SkImageGenerator', 'SkBitmapRegion', 'codec', 'SkWGL', @@ -213,8 +204,9 @@ def generate_separated_sources(platform_sources): 'common': { 'skia/src/gpu/gl/GrGLCreateNativeInterface_none.cpp', 'skia/src/ports/SkDiscardableMemory_none.cpp', - 'skia/src/ports/SkImageDecoder_empty.cpp', 'skia/src/ports/SkMemory_mozalloc.cpp', + 'skia/src/ports/SkImageEncoder_none.cpp', + 'skia/src/ports/SkImageGenerator_none.cpp', }, 'android': { # 'skia/src/ports/SkDebug_android.cpp', @@ -233,7 +225,6 @@ def generate_separated_sources(platform_sources): 'skia/src/opts/SkOpts_sse2.cpp', 'skia/src/opts/SkOpts_ssse3.cpp', 'skia/src/opts/SkOpts_sse41.cpp', - 'skia/src/opts/SkOpts_avx.cpp', }, 'arm': { 'skia/src/core/SkUtilsArm.cpp', @@ -312,9 +303,8 @@ def write_sources(f, values, indent): 'SkBlitRow_opts_arm.cpp', 'SkScan_Antihair.cpp', 'SkParse.cpp', - 'SkSHA1.cpp', - 'SkMD5.cpp', 'SkPictureData.cpp', + 'SkMatrixConvolutionImageFilter.cpp', 'opts_check_x86.cpp', 'GrDrawContext', 'GrResourceCache', @@ -327,6 +317,7 @@ def write_sources(f, values, indent): 'SkParsePath.cpp', 'SkOpts', 'SkRecorder.cpp', + 'SkXfermode', ] def isblacklisted(value): diff --git a/gfx/skia/moz.build b/gfx/skia/moz.build index 14d6d83ee1..604b4ae1e7 100644 --- a/gfx/skia/moz.build +++ b/gfx/skia/moz.build @@ -19,6 +19,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkAAClip.cpp', 'skia/src/core/SkAlphaRuns.cpp', 'skia/src/core/SkAnnotation.cpp', + 'skia/src/core/SkAutoPixmapStorage.cpp', 'skia/src/core/SkBBHFactory.cpp', 'skia/src/core/SkBigPicture.cpp', 'skia/src/core/SkBitmap.cpp', @@ -34,6 +35,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkBlitRow_D16.cpp', 'skia/src/core/SkBlitRow_D32.cpp', 'skia/src/core/SkBlitter.cpp', + 'skia/src/core/SkBlitter_PM4f.cpp', 'skia/src/core/SkBuffer.cpp', 'skia/src/core/SkCachedData.cpp', 'skia/src/core/SkCanvas.cpp', @@ -43,6 +45,8 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkColor.cpp', 'skia/src/core/SkColorFilter.cpp', 'skia/src/core/SkColorFilterShader.cpp', + 'skia/src/core/SkColorMatrixFilterRowMajor255.cpp', + 'skia/src/core/SkColorSpace.cpp', 'skia/src/core/SkColorTable.cpp', 'skia/src/core/SkComposeShader.cpp', 'skia/src/core/SkConfig8888.cpp', @@ -57,6 +61,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkDeviceProfile.cpp', 'skia/src/core/SkDistanceFieldGen.cpp', 'skia/src/core/SkDither.cpp', + 'skia/src/core/SkDocument.cpp', 'skia/src/core/SkDraw.cpp', 'skia/src/core/SkDrawable.cpp', 'skia/src/core/SkDrawLooper.cpp', @@ -84,6 +89,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkImageGenerator.cpp', 'skia/src/core/SkImageInfo.cpp', 'skia/src/core/SkLightingShader.cpp', + 'skia/src/core/SkLinearBitmapPipeline.cpp', 'skia/src/core/SkLineClipper.cpp', 'skia/src/core/SkLocalMatrixImageFilter.cpp', 'skia/src/core/SkLocalMatrixShader.cpp', @@ -101,7 +107,6 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkModeColorFilter.cpp', 'skia/src/core/SkMultiPictureDraw.cpp', 'skia/src/core/SkNinePatchIter.cpp', - 'skia/src/core/SkPackBits.cpp', 'skia/src/core/SkPaint.cpp', 'skia/src/core/SkPaintPriv.cpp', 'skia/src/core/SkPath.cpp', @@ -133,7 +138,6 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkRefDict.cpp', 'skia/src/core/SkRegion.cpp', 'skia/src/core/SkRegion_path.cpp', - 'skia/src/core/SkRemote.cpp', 'skia/src/core/SkResourceCache.cpp', 'skia/src/core/SkRRect.cpp', 'skia/src/core/SkRTree.cpp', @@ -147,7 +151,11 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkSemaphore.cpp', 'skia/src/core/SkShader.cpp', 'skia/src/core/SkSharedMutex.cpp', + 'skia/src/core/SkSpanProcs.cpp', + 'skia/src/core/SkSpecialImage.cpp', + 'skia/src/core/SkSpecialSurface.cpp', 'skia/src/core/SkSpinlock.cpp', + 'skia/src/core/SkSpriteBlitter4f.cpp', 'skia/src/core/SkSpriteBlitter_ARGB32.cpp', 'skia/src/core/SkSpriteBlitter_RGB16.cpp', 'skia/src/core/SkStream.cpp', @@ -156,6 +164,7 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkStroke.cpp', 'skia/src/core/SkStrokeRec.cpp', 'skia/src/core/SkStrokerPriv.cpp', + 'skia/src/core/SkSwizzle.cpp', 'skia/src/core/SkTaskGroup.cpp', 'skia/src/core/SkTextBlob.cpp', 'skia/src/core/SkThreadID.cpp', @@ -171,10 +180,9 @@ UNIFIED_SOURCES += [ 'skia/src/core/SkVertState.cpp', 'skia/src/core/SkWriteBuffer.cpp', 'skia/src/core/SkWriter32.cpp', - 'skia/src/core/SkXfermode.cpp', - 'skia/src/core/SkXfermodeInterpretation.cpp', 'skia/src/core/SkYUVPlanesCache.cpp', - 'skia/src/doc/SkDocument.cpp', + 'skia/src/effects/gradients/Sk4fGradientBase.cpp', + 'skia/src/effects/gradients/Sk4fLinearGradient.cpp', 'skia/src/effects/gradients/SkClampRange.cpp', 'skia/src/effects/gradients/SkGradientBitmapCache.cpp', 'skia/src/effects/gradients/SkGradientShader.cpp', @@ -207,24 +215,21 @@ UNIFIED_SOURCES += [ 'skia/src/effects/SkImageSource.cpp', 'skia/src/effects/SkLayerDrawLooper.cpp', 'skia/src/effects/SkLayerRasterizer.cpp', - 'skia/src/effects/SkLerpXfermode.cpp', 'skia/src/effects/SkLightingImageFilter.cpp', 'skia/src/effects/SkLumaColorFilter.cpp', 'skia/src/effects/SkMagnifierImageFilter.cpp', - 'skia/src/effects/SkMatrixConvolutionImageFilter.cpp', 'skia/src/effects/SkMergeImageFilter.cpp', 'skia/src/effects/SkMorphologyImageFilter.cpp', 'skia/src/effects/SkOffsetImageFilter.cpp', + 'skia/src/effects/SkPackBits.cpp', 'skia/src/effects/SkPaintFlagsDrawFilter.cpp', 'skia/src/effects/SkPaintImageFilter.cpp', 'skia/src/effects/SkPerlinNoiseShader.cpp', 'skia/src/effects/SkPictureImageFilter.cpp', - 'skia/src/effects/SkPixelXorXfermode.cpp', 'skia/src/effects/SkTableColorFilter.cpp', 'skia/src/effects/SkTableMaskFilter.cpp', 'skia/src/effects/SkTestImageFilters.cpp', 'skia/src/effects/SkTileImageFilter.cpp', - 'skia/src/effects/SkXfermodeImageFilter.cpp', 'skia/src/fonts/SkFontMgr_indirect.cpp', 'skia/src/fonts/SkGScalerContext.cpp', 'skia/src/fonts/SkRandomScalerContext.cpp', @@ -236,10 +241,7 @@ UNIFIED_SOURCES += [ 'skia/src/image/SkImageShader.cpp', 'skia/src/image/SkSurface.cpp', 'skia/src/image/SkSurface_Raster.cpp', - 'skia/src/images/bmpdecoderhelper.cpp', - 'skia/src/images/SkDecodingImageGenerator.cpp', - 'skia/src/images/SkPageFlipper.cpp', - 'skia/src/images/SkScaledBitmapSampler.cpp', + 'skia/src/images/SkJPEGWriteUtility.cpp', 'skia/src/lazy/SkDiscardableMemoryPool.cpp', 'skia/src/lazy/SkDiscardablePixelRef.cpp', 'skia/src/pathops/SkAddIntersections.cpp', @@ -275,8 +277,8 @@ UNIFIED_SOURCES += [ 'skia/src/pathops/SkReduceOrder.cpp', 'skia/src/ports/SkDiscardableMemory_none.cpp', 'skia/src/ports/SkGlobalInitialization_default.cpp', - 'skia/src/ports/SkImageDecoder_empty.cpp', - 'skia/src/ports/SkImageGenerator_skia.cpp', + 'skia/src/ports/SkImageEncoder_none.cpp', + 'skia/src/ports/SkImageGenerator_none.cpp', 'skia/src/ports/SkMemory_mozalloc.cpp', 'skia/src/ports/SkOSEnvironment.cpp', 'skia/src/ports/SkOSFile_stdio.cpp', @@ -293,11 +295,11 @@ UNIFIED_SOURCES += [ 'skia/src/utils/SkDumpCanvas.cpp', 'skia/src/utils/SkEventTracer.cpp', 'skia/src/utils/SkFrontBufferedStream.cpp', - 'skia/src/utils/SkImageGeneratorUtils.cpp', 'skia/src/utils/SkInterpolator.cpp', 'skia/src/utils/SkLayer.cpp', 'skia/src/utils/SkMatrix22.cpp', 'skia/src/utils/SkMatrix44.cpp', + 'skia/src/utils/SkMD5.cpp', 'skia/src/utils/SkMeshUtils.cpp', 'skia/src/utils/SkNinePatch.cpp', 'skia/src/utils/SkNullCanvas.cpp', @@ -307,6 +309,7 @@ UNIFIED_SOURCES += [ 'skia/src/utils/SkParseColor.cpp', 'skia/src/utils/SkPatchGrid.cpp', 'skia/src/utils/SkPatchUtils.cpp', + 'skia/src/utils/SkRGBAToYUV.cpp', 'skia/src/utils/SkRTConf.cpp', 'skia/src/utils/SkTextBox.cpp', 'skia/src/utils/SkTextureCompressor.cpp', @@ -327,12 +330,16 @@ SOURCES += [ 'skia/src/core/SkPictureData.cpp', 'skia/src/core/SkRecorder.cpp', 'skia/src/core/SkScan_Antihair.cpp', + 'skia/src/core/SkXfermode.cpp', + 'skia/src/core/SkXfermode4f.cpp', + 'skia/src/core/SkXfermodeInterpretation.cpp', + 'skia/src/core/SkXfermodeU64.cpp', + 'skia/src/effects/SkMatrixConvolutionImageFilter.cpp', + 'skia/src/effects/SkXfermodeImageFilter.cpp', 'skia/src/gpu/gl/GrGLCreateNativeInterface_none.cpp', 'skia/src/pathops/SkPathOpsDebug.cpp', - 'skia/src/utils/SkMD5.cpp', 'skia/src/utils/SkParse.cpp', 'skia/src/utils/SkParsePath.cpp', - 'skia/src/utils/SkSHA1.cpp', ] if CONFIG['MOZ_ENABLE_SKIA_GPU']: UNIFIED_SOURCES += [ @@ -350,6 +357,7 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/batches/GrNinePatch.cpp', 'skia/src/gpu/batches/GrNonAAFillRectBatch.cpp', 'skia/src/gpu/batches/GrNonAAStrokeRectBatch.cpp', + 'skia/src/gpu/batches/GrPLSPathRenderer.cpp', 'skia/src/gpu/batches/GrRectBatchFactory.cpp', 'skia/src/gpu/batches/GrStencilAndCoverPathRenderer.cpp', 'skia/src/gpu/batches/GrTessellatingPathRenderer.cpp', @@ -376,17 +384,7 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/effects/GrTextureDomain.cpp', 'skia/src/gpu/effects/GrTextureStripAtlas.cpp', 'skia/src/gpu/effects/GrXfermodeFragmentProcessor.cpp', - 'skia/src/gpu/effects/GrYUVtoRGBEffect.cpp', - 'skia/src/gpu/gl/debug/GrBufferObj.cpp', - 'skia/src/gpu/gl/debug/GrDebugGL.cpp', - 'skia/src/gpu/gl/debug/GrFrameBufferObj.cpp', - 'skia/src/gpu/gl/debug/GrProgramObj.cpp', - 'skia/src/gpu/gl/debug/GrShaderObj.cpp', - 'skia/src/gpu/gl/debug/GrTextureObj.cpp', - 'skia/src/gpu/gl/debug/GrTextureUnitObj.cpp', - 'skia/src/gpu/gl/debug/SkDebugGLContext.cpp', - 'skia/src/gpu/gl/SkGLContext.cpp', - 'skia/src/gpu/gl/SkNullGLContext.cpp', + 'skia/src/gpu/effects/GrYUVEffect.cpp', 'skia/src/gpu/GrAuditTrail.cpp', 'skia/src/gpu/GrBatchFlushState.cpp', 'skia/src/gpu/GrBatchTest.cpp', @@ -420,6 +418,7 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/GrPathRenderer.cpp', 'skia/src/gpu/GrPathRendererChain.cpp', 'skia/src/gpu/GrPathRendering.cpp', + 'skia/src/gpu/GrPathRenderingDrawContext.cpp', 'skia/src/gpu/GrPathUtils.cpp', 'skia/src/gpu/GrPipeline.cpp', 'skia/src/gpu/GrPipelineBuilder.cpp', @@ -446,6 +445,7 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/GrTextureAccess.cpp', 'skia/src/gpu/GrTextureParamsAdjuster.cpp', 'skia/src/gpu/GrTextureProvider.cpp', + 'skia/src/gpu/GrTextureToYUVPlanes.cpp', 'skia/src/gpu/GrTraceMarker.cpp', 'skia/src/gpu/GrXferProcessor.cpp', 'skia/src/gpu/GrYUVProvider.cpp', @@ -455,13 +455,13 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/SkGrPixelRef.cpp', 'skia/src/gpu/SkGrTexturePixelRef.cpp', 'skia/src/gpu/text/GrAtlasTextBlob.cpp', + 'skia/src/gpu/text/GrAtlasTextBlob_regenInBatch.cpp', 'skia/src/gpu/text/GrAtlasTextContext.cpp', 'skia/src/gpu/text/GrBatchFontCache.cpp', 'skia/src/gpu/text/GrDistanceFieldAdjustTable.cpp', 'skia/src/gpu/text/GrFontScaler.cpp', 'skia/src/gpu/text/GrStencilAndCoverTextContext.cpp', 'skia/src/gpu/text/GrTextBlobCache.cpp', - 'skia/src/gpu/text/GrTextContext.cpp', 'skia/src/gpu/text/GrTextUtils.cpp', 'skia/src/image/SkSurface_Gpu.cpp', ] @@ -477,9 +477,8 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/gl/builders/GrGLProgramBuilder.cpp', 'skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp', 'skia/src/gpu/gl/builders/GrGLSLPrettyPrint.cpp', - 'skia/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp', 'skia/src/gpu/gl/GrGLAssembleInterface.cpp', - 'skia/src/gpu/gl/GrGLBufferImpl.cpp', + 'skia/src/gpu/gl/GrGLBuffer.cpp', 'skia/src/gpu/gl/GrGLCaps.cpp', 'skia/src/gpu/gl/GrGLContext.cpp', 'skia/src/gpu/gl/GrGLCreateNullInterface.cpp', @@ -488,9 +487,7 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/gl/GrGLGLSL.cpp', 'skia/src/gpu/gl/GrGLGpu.cpp', 'skia/src/gpu/gl/GrGLGpuProgramCache.cpp', - 'skia/src/gpu/gl/GrGLIndexBuffer.cpp', 'skia/src/gpu/gl/GrGLInterface.cpp', - 'skia/src/gpu/gl/GrGLNoOpInterface.cpp', 'skia/src/gpu/gl/GrGLPath.cpp', 'skia/src/gpu/gl/GrGLPathRange.cpp', 'skia/src/gpu/gl/GrGLPathRendering.cpp', @@ -499,14 +496,13 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/gl/GrGLProgramDesc.cpp', 'skia/src/gpu/gl/GrGLRenderTarget.cpp', 'skia/src/gpu/gl/GrGLStencilAttachment.cpp', + 'skia/src/gpu/gl/GrGLTestInterface.cpp', 'skia/src/gpu/gl/GrGLTexture.cpp', 'skia/src/gpu/gl/GrGLTextureRenderTarget.cpp', - 'skia/src/gpu/gl/GrGLTransferBuffer.cpp', 'skia/src/gpu/gl/GrGLUniformHandler.cpp', 'skia/src/gpu/gl/GrGLUtil.cpp', 'skia/src/gpu/gl/GrGLVaryingHandler.cpp', 'skia/src/gpu/gl/GrGLVertexArray.cpp', - 'skia/src/gpu/gl/GrGLVertexBuffer.cpp', 'skia/src/gpu/glsl/GrGLSL.cpp', 'skia/src/gpu/glsl/GrGLSLBlend.cpp', 'skia/src/gpu/glsl/GrGLSLCaps.cpp', @@ -516,6 +512,7 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']: 'skia/src/gpu/glsl/GrGLSLGeometryShaderBuilder.cpp', 'skia/src/gpu/glsl/GrGLSLPrimitiveProcessor.cpp', 'skia/src/gpu/glsl/GrGLSLProgramBuilder.cpp', + 'skia/src/gpu/glsl/GrGLSLProgramDataManager.cpp', 'skia/src/gpu/glsl/GrGLSLShaderBuilder.cpp', 'skia/src/gpu/glsl/GrGLSLUtil.cpp', 'skia/src/gpu/glsl/GrGLSLVarying.cpp', @@ -531,7 +528,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gonk'): 'skia/src/ports/SkDebug_android.cpp', 'skia/src/ports/SkOSFile_posix.cpp', 'skia/src/ports/SkOSLibrary_posix.cpp', - 'skia/src/ports/SkTime_Unix.cpp', 'skia/src/ports/SkTLS_pthread.cpp', 'skia/src/utils/SkThreadUtils_pthread.cpp', ] @@ -544,7 +540,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] in {'cocoa', 'uikit'}: 'skia/src/ports/SkDebug_stdio.cpp', 'skia/src/ports/SkOSFile_posix.cpp', 'skia/src/ports/SkOSLibrary_posix.cpp', - 'skia/src/ports/SkTime_Unix.cpp', 'skia/src/ports/SkTLS_pthread.cpp', 'skia/src/utils/mac/SkCreateCGImageRef.cpp', 'skia/src/utils/mac/SkStream_mac.cpp', @@ -558,7 +553,6 @@ if CONFIG['MOZ_WIDGET_GTK']: 'skia/src/ports/SkDebug_stdio.cpp', 'skia/src/ports/SkOSFile_posix.cpp', 'skia/src/ports/SkOSLibrary_posix.cpp', - 'skia/src/ports/SkTime_Unix.cpp', 'skia/src/ports/SkTLS_pthread.cpp', 'skia/src/utils/SkThreadUtils_pthread.cpp', ] @@ -571,7 +565,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt': 'skia/src/ports/SkDebug_stdio.cpp', 'skia/src/ports/SkOSFile_posix.cpp', 'skia/src/ports/SkOSLibrary_posix.cpp', - 'skia/src/ports/SkTime_Unix.cpp', 'skia/src/ports/SkTLS_pthread.cpp', 'skia/src/utils/SkThreadUtils_pthread.cpp', ] @@ -589,7 +582,6 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': 'skia/src/ports/SkOSLibrary_win.cpp', 'skia/src/ports/SkRemotableFontMgr_win_dw.cpp', 'skia/src/ports/SkScalerContext_win_dw.cpp', - 'skia/src/ports/SkTime_win.cpp', 'skia/src/ports/SkTLS_win.cpp', 'skia/src/ports/SkTypeface_win_dw.cpp', 'skia/src/utils/SkThreadUtils_win.cpp', @@ -607,8 +599,6 @@ if CONFIG['INTEL_ARCHITECTURE']: 'skia/src/opts/SkBitmapProcState_opts_SSE2.cpp', 'skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp', 'skia/src/opts/SkBlitRow_opts_SSE2.cpp', - 'skia/src/opts/SkBlitRow_opts_SSE4.cpp', - 'skia/src/opts/SkOpts_avx.cpp', 'skia/src/opts/SkOpts_sse2.cpp', 'skia/src/opts/SkOpts_sse41.cpp', 'skia/src/opts/SkOpts_ssse3.cpp', @@ -709,29 +699,23 @@ if CONFIG['_MSC_VER']: SOURCES['skia/src/opts/SkBitmapProcState_opts_SSE2.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=20'] SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=31'] SOURCES['skia/src/opts/SkBlitRow_opts_SSE2.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=20'] - SOURCES['skia/src/opts/SkBlitRow_opts_SSE4.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=41'] SOURCES['skia/src/opts/SkOpts_sse2.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=20'] SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=31'] SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['/arch:SSE2 -DSK_CPU_SSE_LEVEL=41'] - SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['/arch:AVX -DSK_CPU_SSE_LEVEL=51'] if CONFIG['INTEL_ARCHITECTURE'] and CONFIG['GNU_CC']: SOURCES['skia/src/opts/SkBitmapFilter_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] SOURCES['skia/src/opts/SkBitmapProcState_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3'] SOURCES['skia/src/opts/SkBlitRow_opts_SSE2.cpp'].flags += CONFIG['SSE2_FLAGS'] - SOURCES['skia/src/opts/SkBlitRow_opts_SSE4.cpp'].flags += ['-msse4.1'] SOURCES['skia/src/opts/SkOpts_sse2.cpp'].flags += CONFIG['SSE2_FLAGS'] SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['-mssse3'] SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1'] - SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx'] elif CONFIG['CPU_ARCH'] == 'arm' and CONFIG['GNU_CC'] and CONFIG['BUILD_ARM_NEON']: DEFINES['SK_ARM_HAS_OPTIONAL_NEON'] = 1 elif CONFIG['CLANG_CL']: SOURCES['skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp'].flags += ['-mssse3'] - SOURCES['skia/src/opts/SkBlitRow_opts_SSE4.cpp'].flags += ['-msse4.1'] SOURCES['skia/src/opts/SkOpts_ssse3.cpp'].flags += ['-mssse3'] SOURCES['skia/src/opts/SkOpts_sse41.cpp'].flags += ['-msse4.1'] - SOURCES['skia/src/opts/SkOpts_avx.cpp'].flags += ['-mavx'] if CONFIG['GNU_CXX'] and CONFIG['CPU_ARCH'] == 'arm': SOURCES['skia/src/opts/SkBlitRow_opts_arm.cpp'].flags += ['-fomit-frame-pointer'] diff --git a/gfx/skia/skia/include/animator/SkAnimator.h b/gfx/skia/skia/include/animator/SkAnimator.h index 9bb3642ca0..0fe787c526 100644 --- a/gfx/skia/skia/include/animator/SkAnimator.h +++ b/gfx/skia/skia/include/animator/SkAnimator.h @@ -443,7 +443,8 @@ public: }; /** Sets a user class to return the current time to the animator. - Optional; if not called, the system clock will be used by calling SkTime::GetMSecs instead. + Optional; if not called, the system clock will be used by calling + SkEvent::GetMSecsSinceStartup instead. @param callBack the time function */ void setTimeline(const Timeline& ); diff --git a/gfx/skia/skia/include/c/sk_canvas.h b/gfx/skia/skia/include/c/sk_canvas.h index f6c0d4e1c1..1e1dd24f99 100644 --- a/gfx/skia/skia/include/c/sk_canvas.h +++ b/gfx/skia/skia/include/c/sk_canvas.h @@ -102,6 +102,11 @@ SK_API void sk_canvas_draw_paint(sk_canvas_t*, const sk_paint_t*); paint. */ SK_API void sk_canvas_draw_rect(sk_canvas_t*, const sk_rect_t*, const sk_paint_t*); +/** + * Draw the circle centered at (cx, cy) with radius rad using the specified paint. + * The circle will be filled or framed based on the style in the paint + */ +SK_API void sk_canvas_draw_circle(sk_canvas_t*, float cx, float cy, float rad, const sk_paint_t*); /** Draw the specified oval using the specified paint. The oval will be filled or framed based on the style in the paint diff --git a/gfx/skia/skia/include/codec/SkAndroidCodec.h b/gfx/skia/skia/include/codec/SkAndroidCodec.h index c5578d3964..7fee5be255 100644 --- a/gfx/skia/skia/include/codec/SkAndroidCodec.h +++ b/gfx/skia/skia/include/codec/SkAndroidCodec.h @@ -143,6 +143,8 @@ public: /** * Indicates is destination pixel memory is zero initialized. + * + * The default is SkCodec::kNo_ZeroInitialized. */ SkCodec::ZeroInitialized fZeroInitialized; @@ -153,6 +155,8 @@ public: * * If the EncodedFormat is kWEBP_SkEncodedFormat, the top and left * values must be even. + * + * The default is NULL, meaning a decode of the entire image. */ SkIRect* fSubset; @@ -166,6 +170,8 @@ public: * If the client does not request kIndex8_SkColorType, then the last * two parameters may be NULL. If fColorCount is not null, it will be * set to 0. + * + * The default is NULL for both pointers. */ SkPMColor* fColorPtr; int* fColorCount; @@ -174,6 +180,8 @@ public: * The client may provide an integer downscale factor for the decode. * The codec may implement this downscaling by sampling or another * method if it is more efficient. + * + * The default is 1, representing no downscaling. */ int fSampleSize; }; @@ -207,7 +215,8 @@ public: * be nullptr. * * The AndroidOptions object is also used to specify any requested scaling or subsetting - * using options->fSampleSize and options->fSubset. + * using options->fSampleSize and options->fSubset. If NULL, the defaults (as specified above + * for AndroidOptions) are used. * * @return Result kSuccess, or another value explaining the type of failure. */ @@ -219,13 +228,18 @@ public: const AndroidOptions* options); /** - * Simplified version of getAndroidPixels() where we supply the default AndroidOptions. + * Simplified version of getAndroidPixels() where we supply the default AndroidOptions as + * specified above for AndroidOptions. * * This will return an error if the info is kIndex_8_SkColorType and also will not perform * any scaling or subsetting. */ SkCodec::Result getAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes); + SkCodec::Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes) { + return this->getAndroidPixels(info, pixels, rowBytes); + } + protected: SkAndroidCodec(SkCodec*); diff --git a/gfx/skia/skia/include/codec/SkCodec.h b/gfx/skia/skia/include/codec/SkCodec.h index 597ebd039e..629274d21c 100644 --- a/gfx/skia/skia/include/codec/SkCodec.h +++ b/gfx/skia/skia/include/codec/SkCodec.h @@ -15,7 +15,9 @@ #include "SkSize.h" #include "SkStream.h" #include "SkTypes.h" +#include "SkYUVSizeInfo.h" +class SkColorSpace; class SkData; class SkPngChunkReader; class SkSampler; @@ -98,6 +100,32 @@ public: */ const SkImageInfo& getInfo() const { return fSrcInfo; } + /** + * Returns the color space associated with the codec. + * Does not affect ownership. + * Might be NULL. + */ + SkColorSpace* getColorSpace() const { return fColorSpace.get(); } + + enum Origin { + kTopLeft_Origin = 1, // Default + kTopRight_Origin = 2, // Reflected across y-axis + kBottomRight_Origin = 3, // Rotated 180 + kBottomLeft_Origin = 4, // Reflected across x-axis + kLeftTop_Origin = 5, // Reflected across x-axis, Rotated 90 CCW + kRightTop_Origin = 6, // Rotated 90 CW + kRightBottom_Origin = 7, // Reflected across x-axis, Rotated 90 CW + kLeftBottom_Origin = 8, // Rotated 90 CCW + kDefault_Origin = kTopLeft_Origin, + kLast_Origin = kLeftBottom_Origin, + }; + + /** + * Returns the image orientation stored in the EXIF data. + * If there is no EXIF data, or if we cannot read the EXIF data, returns kTopLeft. + */ + Origin getOrigin() const { return fOrigin; } + /** * Return a size that approximately supports the desired scale factor. * The codec may not be able to scale efficiently to the exact scale @@ -277,6 +305,46 @@ public: */ Result getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes); + /** + * If decoding to YUV is supported, this returns true. Otherwise, this + * returns false and does not modify any of the parameters. + * + * @param sizeInfo Output parameter indicating the sizes and required + * allocation widths of the Y, U, and V planes. + * @param colorSpace Output parameter. If non-NULL this is set to kJPEG, + * otherwise this is ignored. + */ + bool queryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const { + if (nullptr == sizeInfo) { + return false; + } + + return this->onQueryYUV8(sizeInfo, colorSpace); + } + + /** + * Returns kSuccess, or another value explaining the type of failure. + * This always attempts to perform a full decode. If the client only + * wants size, it should call queryYUV8(). + * + * @param sizeInfo Needs to exactly match the values returned by the + * query, except the WidthBytes may be larger than the + * recommendation (but not smaller). + * @param planes Memory for each of the Y, U, and V planes. + */ + Result getYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) { + if (nullptr == planes || nullptr == planes[0] || nullptr == planes[1] || + nullptr == planes[2]) { + return kInvalidInput; + } + + if (!this->rewindIfNeeded()) { + return kCouldNotRewind; + } + + return this->onGetYUV8Planes(sizeInfo, planes); + } + /** * The remaining functions revolve around decoding scanlines. */ @@ -440,9 +508,15 @@ public: int outputScanline(int inputScanline) const; protected: - SkCodec(const SkImageInfo&, SkStream*); + /** + * Takes ownership of SkStream* + */ + SkCodec(const SkImageInfo&, + SkStream*, + sk_sp = nullptr, + Origin = kTopLeft_Origin); - virtual SkISize onGetScaledDimensions(float /* desiredScale */) const { + virtual SkISize onGetScaledDimensions(float /*desiredScale*/) const { // By default, scaling is not supported. return this->getInfo().dimensions(); } @@ -469,7 +543,15 @@ protected: SkPMColor ctable[], int* ctableCount, int* rowsDecoded) = 0; - virtual bool onGetValidSubset(SkIRect* /* desiredSubset */) const { + virtual bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const { + return false; + } + + virtual Result onGetYUV8Planes(const SkYUVSizeInfo&, void*[3] /*planes*/) { + return kUnimplemented; + } + + virtual bool onGetValidSubset(SkIRect* /*desiredSubset*/) const { // By default, subsets are not supported. return false; } @@ -500,15 +582,14 @@ protected: * scanlines. This allows the subclass to indicate what value to fill with. * * @param colorType Destination color type. - * @param alphaType Destination alpha type. * @return The value with which to fill uninitialized pixels. * * Note that we can interpret the return value as an SkPMColor, a 16-bit 565 color, * an 8-bit gray color, or an 8-bit index into a color table, depending on the color * type. */ - uint32_t getFillValue(SkColorType colorType, SkAlphaType alphaType) const { - return this->onGetFillValue(colorType, alphaType); + uint32_t getFillValue(SkColorType colorType) const { + return this->onGetFillValue(colorType); } /** @@ -516,13 +597,13 @@ protected: * types that we support. Note that for color types that do not use the full 32-bits, * we will simply take the low bits of the fill value. * - * kN32_SkColorType: Transparent or Black + * kN32_SkColorType: Transparent or Black, depending on the src alpha type * kRGB_565_SkColorType: Black * kGray_8_SkColorType: Black * kIndex_8_SkColorType: First color in color table */ - virtual uint32_t onGetFillValue(SkColorType /*colorType*/, SkAlphaType alphaType) const { - return kOpaque_SkAlphaType == alphaType ? SK_ColorBLACK : SK_ColorTRANSPARENT; + virtual uint32_t onGetFillValue(SkColorType /*colorType*/) const { + return kOpaque_SkAlphaType == fSrcInfo.alphaType() ? SK_ColorBLACK : SK_ColorTRANSPARENT; } /** @@ -561,13 +642,16 @@ protected: virtual int onOutputScanline(int inputScanline) const; private: - const SkImageInfo fSrcInfo; - SkAutoTDelete fStream; - bool fNeedsRewind; + const SkImageInfo fSrcInfo; + SkAutoTDelete fStream; + bool fNeedsRewind; + sk_sp fColorSpace; + const Origin fOrigin; + // These fields are only meaningful during scanline decodes. - SkImageInfo fDstInfo; - SkCodec::Options fOptions; - int fCurrScanline; + SkImageInfo fDstInfo; + SkCodec::Options fOptions; + int fCurrScanline; /** * Return whether these dimensions are supported as a scale. @@ -588,18 +672,7 @@ private: return kUnimplemented; } - // Naive default version just calls onGetScanlines on temp memory. - virtual bool onSkipScanlines(int countLines) { - // FIXME (msarett): Make this a pure virtual and always override this. - SkAutoMalloc storage(fDstInfo.minRowBytes()); - - // Note that we pass 0 to rowBytes so we continue to use the same memory. - // Also note that while getScanlines checks that rowBytes is big enough, - // onGetScanlines bypasses that check. - // Calling the virtual method also means we do not double count - // countLines. - return countLines == this->onGetScanlines(storage.get(), countLines, 0); - } + virtual bool onSkipScanlines(int /*countLines*/) { return false; } virtual int onGetScanlines(void* /*dst*/, int /*countLines*/, size_t /*rowBytes*/) { return 0; } diff --git a/gfx/skia/skia/include/codec/SkEncodedFormat.h b/gfx/skia/skia/include/codec/SkEncodedFormat.h index 003159a8de..c097e088f2 100644 --- a/gfx/skia/skia/include/codec/SkEncodedFormat.h +++ b/gfx/skia/skia/include/codec/SkEncodedFormat.h @@ -23,5 +23,6 @@ enum SkEncodedFormat { kPKM_SkEncodedFormat, kKTX_SkEncodedFormat, kASTC_SkEncodedFormat, + kDNG_SkEncodedFormat, }; #endif // SkEncodedFormat_DEFINED diff --git a/gfx/skia/skia/include/config/SkUserConfig.h b/gfx/skia/skia/include/config/SkUserConfig.h index c0186122e2..4c18c5ef1e 100644 --- a/gfx/skia/skia/include/config/SkUserConfig.h +++ b/gfx/skia/skia/include/config/SkUserConfig.h @@ -58,13 +58,6 @@ //#define SK_DEBUG_GLYPH_CACHE //#define SK_DEBUG_PATH -/* If, in debugging mode, Skia needs to stop (presumably to invoke a debugger) - it will call SK_CRASH(). If this is not defined it, it is defined in - SkPostConfig.h to write to an illegal address - */ -//#define SK_CRASH() *(int *)(uintptr_t)0 = 0 - - /* preconfig will have attempted to determine the endianness of the system, but you can change these mutually exclusive flags here. */ @@ -100,11 +93,6 @@ */ //#define SK_DEFAULT_IMAGE_CACHE_LIMIT (1024 * 1024) -/* Define this to allow PDF scalars above 32k. The PDF/A spec doesn't allow - them, but modern PDF interpreters should handle them just fine. - */ -//#define SK_ALLOW_LARGE_PDF_SCALARS - /* Define this to provide font subsetter in PDF generation. */ //#define SK_SFNTLY_SUBSETTER "sfntly/subsetter/font_subsetter.h" @@ -143,15 +131,13 @@ */ //#define SK_SUPPORT_GPU 1 - -/* The PDF generation code uses Path Ops to handle complex clipping paths, - * but at this time, Path Ops is not release ready yet. So, the code is - * hidden behind this #define guard. If you are feeling adventurous and - * want the latest and greatest PDF generation code, uncomment the #define. - * When Path Ops is release ready, the define guards and this user config - * define should be removed entirely. +/* Skia makes use of histogram logging macros to trace the frequency of + * events. By default, Skia provides no-op versions of these macros. + * Skia consumers can provide their own definitions of these macros to + * integrate with their histogram collection backend. */ -//#define SK_PDF_USE_PATHOPS_CLIPPING +//#define SK_HISTOGRAM_BOOLEAN(name, value) +//#define SK_HISTOGRAM_ENUMERATION(name, value, boundary_value) // On all platforms we have this byte order #define SK_A32_SHIFT 24 @@ -168,8 +154,6 @@ #define SK_RASTERIZE_EVEN_ROUNDING -#define GR_GL_PER_GL_FUNC_CALLBACK 1 - #define MOZ_SKIA 1 #ifndef MOZ_IMPLICIT @@ -180,4 +164,15 @@ # endif #endif +/* Check if building with either MSVC, libc++, or a sufficiently recent version of libstdc++. ++ * On platforms like OS X 10.6 or older Android SDKs, we need to work around a lack of certain ++ * C++11 features. ++ */ +#include "mozilla/Compiler.h" +#if MOZ_IS_MSVC || MOZ_USING_LIBCXX || MOZ_LIBSTDCXX_VERSION_AT_LEAST(4, 8, 0) +# define MOZ_SKIA_AVOID_CXX11 0 +#else +# define MOZ_SKIA_AVOID_CXX11 1 +#endif + #endif diff --git a/gfx/skia/skia/include/core/SkAnnotation.h b/gfx/skia/skia/include/core/SkAnnotation.h index 80503c78d2..35cc2b5d04 100644 --- a/gfx/skia/skia/include/core/SkAnnotation.h +++ b/gfx/skia/skia/include/core/SkAnnotation.h @@ -8,83 +8,18 @@ #ifndef SkAnnotation_DEFINED #define SkAnnotation_DEFINED -#include "SkRefCnt.h" -#include "SkString.h" #include "SkTypes.h" class SkData; -class SkReadBuffer; -class SkWriteBuffer; struct SkPoint; - -/** - * Experimental class for annotating draws. Do not use directly yet. - * Use helper functions at the bottom of this file for now. - */ -class SkAnnotation : public SkRefCnt { -public: - virtual ~SkAnnotation(); - - static SkAnnotation* Create(const char key[], SkData* value) { - return new SkAnnotation(key, value); - } - - static SkAnnotation* Create(SkReadBuffer& buffer) { return new SkAnnotation(buffer); } - - /** - * Return the data for the specified key, or NULL. - */ - SkData* find(const char key[]) const; - - void writeToBuffer(SkWriteBuffer&) const; - -private: - SkAnnotation(const char key[], SkData* value); - SkAnnotation(SkReadBuffer&); - - SkString fKey; - SkData* fData; - - typedef SkRefCnt INHERITED; -}; - -/** - * Experimental collection of predefined Keys into the Annotation dictionary - */ -class SkAnnotationKeys { -public: - /** - * Returns the canonical key whose payload is a URL - */ - static const char* URL_Key(); - - /** - * Returns the canonical key whose payload is the name of a destination to - * be defined. - */ - static const char* Define_Named_Dest_Key(); - - /** - * Returns the canonical key whose payload is the name of a destination to - * be linked to. - */ - static const char* Link_Named_Dest_Key(); -}; - -/////////////////////////////////////////////////////////////////////////////// -// -// Experimental helper functions to use Annotations -// - struct SkRect; class SkCanvas; /** - * Experimental! - * * Annotate the canvas by associating the specified URL with the - * specified rectangle (in local coordinates, just like drawRect). If the - * backend of this canvas does not support annotations, this call is + * specified rectangle (in local coordinates, just like drawRect). + * + * If the backend of this canvas does not support annotations, this call is * safely ignored. * * The caller is responsible for managing its ownership of the SkData. @@ -92,8 +27,6 @@ class SkCanvas; SK_API void SkAnnotateRectWithURL(SkCanvas*, const SkRect&, SkData*); /** - * Experimental! - * * Annotate the canvas by associating a name with the specified point. * * If the backend of this canvas does not support annotations, this call is @@ -104,8 +37,6 @@ SK_API void SkAnnotateRectWithURL(SkCanvas*, const SkRect&, SkData*); SK_API void SkAnnotateNamedDestination(SkCanvas*, const SkPoint&, SkData*); /** - * Experimental! - * * Annotate the canvas by making the specified rectangle link to a named * destination. * @@ -116,5 +47,4 @@ SK_API void SkAnnotateNamedDestination(SkCanvas*, const SkPoint&, SkData*); */ SK_API void SkAnnotateLinkToDestination(SkCanvas*, const SkRect&, SkData*); - #endif diff --git a/gfx/skia/skia/include/core/SkBitmapDevice.h b/gfx/skia/skia/include/core/SkBitmapDevice.h index d1cb9ad0f8..b53bfd0622 100644 --- a/gfx/skia/skia/include/core/SkBitmapDevice.h +++ b/gfx/skia/skia/include/core/SkBitmapDevice.h @@ -160,7 +160,7 @@ private: SkBaseDevice* onCreateDevice(const CreateInfo&, const SkPaint*) override; - SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) override; + sk_sp makeSurface(const SkImageInfo&, const SkSurfaceProps&) override; SkImageFilter::Cache* getImageFilterCache() override; diff --git a/gfx/skia/skia/include/core/SkCanvas.h b/gfx/skia/skia/include/core/SkCanvas.h index d1de626315..8e675b42fc 100644 --- a/gfx/skia/skia/include/core/SkCanvas.h +++ b/gfx/skia/skia/include/core/SkCanvas.h @@ -11,6 +11,7 @@ #include "SkTypes.h" #include "SkBitmap.h" #include "SkDeque.h" +#include "SkImage.h" #include "SkPaint.h" #include "SkRefCnt.h" #include "SkRegion.h" @@ -22,10 +23,10 @@ class GrRenderTarget; class SkBaseDevice; class SkCanvasClipVisitor; class SkClipStack; +class SkData; class SkDraw; class SkDrawable; class SkDrawFilter; -class SkImage; class SkImageFilter; class SkMetaData; class SkPath; @@ -37,16 +38,6 @@ class SkSurface; class SkSurface_Base; class SkTextBlob; -/* - * If you want the legacy cliptolayer flag (i.e. android), then you must have the new - * legacy saveflags. - */ -#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG -#ifndef SK_SUPPORT_LEGACY_SAVEFLAGS - #define SK_SUPPORT_LEGACY_SAVEFLAGS -#endif -#endif - /** \class SkCanvas A Canvas encapsulates all of the state about drawing into a device (bitmap). @@ -64,7 +55,7 @@ class SkTextBlob; */ class SK_API SkCanvas : public SkRefCnt { enum PrivateSaveLayerFlags { - kDontClipToLayer_PrivateSaveLayerFlag = 1 << 31, + kDontClipToLayer_PrivateSaveLayerFlag = 1U << 31, }; public: @@ -131,10 +122,19 @@ public: */ SkImageInfo imageInfo() const; + /** + * If the canvas is backed by pixels (cpu or gpu), this writes a copy of the SurfaceProps + * for the canvas to the location supplied by the caller, and returns true. Otherwise, + * return false and leave the supplied props unchanged. + */ + bool getProps(SkSurfaceProps*) const; + /////////////////////////////////////////////////////////////////////////// /** - * Trigger the immediate execution of all pending draw operations. + * Trigger the immediate execution of all pending draw operations. For the GPU + * backend this will resolve all rendering to the GPU surface backing the + * SkSurface that owns this canvas. */ void flush(); @@ -193,7 +193,10 @@ public: * inherits the properties of the surface that owns this canvas. If this canvas has no parent * surface, then the new surface is created with default properties. */ - SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps* = NULL); + sk_sp makeSurface(const SkImageInfo&, const SkSurfaceProps* = nullptr); +#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API + SkSurface* newSurface(const SkImageInfo& info, const SkSurfaceProps* props = NULL); +#endif /** * Return the GPU context of the device that is associated with the canvas. @@ -219,16 +222,19 @@ public: /** * If the canvas has readable pixels in its base layer (and is not recording to a picture * or other non-raster target) and has direct access to its pixels (i.e. they are in - * local RAM) return the const-address of those pixels, and if not null, - * return the ImageInfo and rowBytes. The returned address is only valid + * local RAM) return true, and if not null, return in the pixmap parameter information about + * the pixels. The pixmap's pixel address is only valid * while the canvas object is in scope and unchanged. Any API calls made on - * canvas (or its parent surface if any) will invalidate the - * returned address (and associated information). + * canvas (or its parent surface if any) will invalidate the pixel address + * (and associated information). * - * On failure, returns NULL and the info and rowBytes parameters are - * ignored. + * On failure, returns false and the pixmap parameter will be ignored. */ + bool peekPixels(SkPixmap*); + +#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS const void* peekPixels(SkImageInfo* info, size_t* rowBytes); +#endif /** * Copy the pixels from the base-layer into the specified buffer (pixels + rowBytes), @@ -295,35 +301,6 @@ public: /////////////////////////////////////////////////////////////////////////// -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS - enum SaveFlags { - /** save the matrix state, restoring it on restore() */ - // [deprecated] kMatrix_SaveFlag = 0x01, - kMatrix_SaveFlag = 0x01, - /** save the clip state, restoring it on restore() */ - // [deprecated] kClip_SaveFlag = 0x02, - kClip_SaveFlag = 0x02, - /** the layer needs to support per-pixel alpha */ - kHasAlphaLayer_SaveFlag = 0x04, - /** the layer needs to support 8-bits per color component */ - kFullColorLayer_SaveFlag = 0x08, - /** - * the layer should clip against the bounds argument - * - * if SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG is undefined, this is treated as always on. - */ - kClipToLayer_SaveFlag = 0x10, - - // helper masks for common choices - // [deprecated] kMatrixClip_SaveFlag = 0x03, - kMatrixClip_SaveFlag = 0x03, -#ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG - kARGB_NoClipLayer_SaveFlag = 0x0F, -#endif - kARGB_ClipLayer_SaveFlag = 0x1F - }; -#endif - /** This call saves the current matrix, clip, and drawFilter, and pushes a copy onto a private stack. Subsequent calls to translate, scale, rotate, skew, concat or clipRect, clipPath, and setDrawFilter all @@ -359,26 +336,6 @@ public: */ int saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint); -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS - /** DEPRECATED - use saveLayer(const SkRect*, const SkPaint*) instead. - - This behaves the same as saveLayer(const SkRect*, const SkPaint*), - but it allows fine-grained control of which state bits to be saved - (and subsequently restored). - - @param bounds (may be null) This rect, if non-null, is used as a hint to - limit the size of the offscreen, and thus drawing may be - clipped to it, though that clipping is not guaranteed to - happen. If exact clipping is desired, use clipRect(). - @param paint (may be null) This is copied, and is applied to the - offscreen when restore() is called - @param flags LayerFlags - @return The value to pass to restoreToCount() to balance this save() - */ - SK_ATTR_EXTERNALLY_DEPRECATED("SaveFlags use is deprecated") - int saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags); -#endif - /** This behaves the same as save(), but in addition it allocates an offscreen bitmap. All drawing calls are directed there, and only when the balancing call to restore() is made is that offscreen transfered to @@ -392,25 +349,6 @@ public: */ int saveLayerAlpha(const SkRect* bounds, U8CPU alpha); -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS - /** DEPRECATED - use saveLayerAlpha(const SkRect*, U8CPU) instead. - - This behaves the same as saveLayerAlpha(const SkRect*, U8CPU), - but it allows fine-grained control of which state bits to be saved - (and subsequently restored). - - @param bounds (may be null) This rect, if non-null, is used as a hint to - limit the size of the offscreen, and thus drawing may be - clipped to it, though that clipping is not guaranteed to - happen. If exact clipping is desired, use clipRect(). - @param alpha This is applied to the offscreen when restore() is called. - @param flags LayerFlags - @return The value to pass to restoreToCount() to balance this save() - */ - SK_ATTR_EXTERNALLY_DEPRECATED("SaveFlags use is deprecated") - int saveLayerAlpha(const SkRect* bounds, U8CPU alpha, SaveFlags flags); -#endif - enum { kIsOpaque_SaveLayerFlag = 1 << 0, kPreserveLCDText_SaveLayerFlag = 1 << 1, @@ -844,6 +782,10 @@ public: @param paint The paint used to draw the image, or NULL */ void drawImage(const SkImage* image, SkScalar left, SkScalar top, const SkPaint* paint = NULL); + void drawImage(const sk_sp& image, SkScalar left, SkScalar top, + const SkPaint* paint = NULL) { + this->drawImage(image.get(), left, top, paint); + } /** * Controls the behavior at the edge of the src-rect, when specified in drawImageRect, @@ -895,6 +837,20 @@ public: void drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint, SrcRectConstraint = kStrict_SrcRectConstraint); + void drawImageRect(const sk_sp& image, const SkRect& src, const SkRect& dst, + const SkPaint* paint, + SrcRectConstraint constraint = kStrict_SrcRectConstraint) { + this->drawImageRect(image.get(), src, dst, paint, constraint); + } + void drawImageRect(const sk_sp& image, const SkIRect& isrc, const SkRect& dst, + const SkPaint* paint, SrcRectConstraint cons = kStrict_SrcRectConstraint) { + this->drawImageRect(image.get(), isrc, dst, paint, cons); + } + void drawImageRect(const sk_sp& image, const SkRect& dst, const SkPaint* paint, + SrcRectConstraint cons = kStrict_SrcRectConstraint) { + this->drawImageRect(image.get(), dst, paint, cons); + } + /** * Draw the image stretched differentially to fit into dst. * center is a rect within the image, and logically divides the image @@ -910,7 +866,11 @@ public: * - The sides (along the shrink axis) and center are not drawn */ void drawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, - const SkPaint* paint = NULL); + const SkPaint* paint = nullptr); + void drawImageNine(const sk_sp& image, const SkIRect& center, const SkRect& dst, + const SkPaint* paint = nullptr) { + this->drawImageNine(image.get(), center, dst, paint); + } /** Draw the specified bitmap, with its top/left corner at (x,y), using the specified paint, transformed by the current matrix. Note: if the paint @@ -1045,6 +1005,9 @@ public: void drawPicture(const SkPicture* picture) { this->drawPicture(picture, NULL, NULL); } + void drawPicture(const sk_sp& picture) { + this->drawPicture(picture.get()); + } /** * Draw the picture into this canvas. @@ -1059,6 +1022,9 @@ public: * saveLayer(paint)/drawPicture/restore */ void drawPicture(const SkPicture*, const SkMatrix* matrix, const SkPaint* paint); + void drawPicture(const sk_sp& picture, const SkMatrix* matrix, const SkPaint* paint) { + this->drawPicture(picture.get(), matrix, paint); + } enum VertexMode { kTriangles_VertexMode, @@ -1094,6 +1060,14 @@ public: const SkColor colors[], SkXfermode* xmode, const uint16_t indices[], int indexCount, const SkPaint& paint); + void drawVertices(VertexMode vmode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], const sk_sp& xmode, + const uint16_t indices[], int indexCount, + const SkPaint& paint) { + this->drawVertices(vmode, vertexCount, vertices, texs, colors, xmode.get(), + indices, indexCount, paint); + } /** Draw a cubic coons patch @@ -1110,6 +1084,10 @@ public: */ void drawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint); + void drawPatch(const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], + const sk_sp& xmode, const SkPaint& paint) { + this->drawPatch(cubics, colors, texCoords, xmode.get(), paint); + } /** * Draw a set of sprites from the atlas. Each is specified by a tex rectangle in the @@ -1137,6 +1115,17 @@ public: this->drawAtlas(atlas, xform, tex, NULL, count, SkXfermode::kDst_Mode, cullRect, paint); } + void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], + const SkColor colors[], int count, SkXfermode::Mode mode, const SkRect* cull, + const SkPaint* paint) { + this->drawAtlas(atlas.get(), xform, tex, colors, count, mode, cull, paint); + } + void drawAtlas(const sk_sp& atlas, const SkRSXform xform[], const SkRect tex[], + int count, const SkRect* cullRect, const SkPaint* paint) { + this->drawAtlas(atlas.get(), xform, tex, nullptr, count, SkXfermode::kDst_Mode, + cullRect, paint); + } + /** * Draw the contents of this drawable into the canvas. If the canvas is async * (e.g. it is recording into a picture) then the drawable will be referenced instead, @@ -1148,12 +1137,34 @@ public: void drawDrawable(SkDrawable* drawable, const SkMatrix* = NULL); void drawDrawable(SkDrawable*, SkScalar x, SkScalar y); - ////////////////////////////////////////////////////////////////////////// + /** + * Send an "annotation" to the canvas. The annotation is a key/value pair, where the key is + * a null-terminated utf8 string, and the value is a blob of data stored in an SkData + * (which may be null). The annotation is associated with the specified rectangle. + * + * The caller still retains its ownership of the data (if any). + * + * Note: on may canvas types, this information is ignored, but some canvases (e.g. recording + * a picture or drawing to a PDF document) will pass on this information. + */ + void drawAnnotation(const SkRect&, const char key[], SkData* value); + void drawAnnotation(const SkRect& rect, const char key[], const sk_sp& value) { + this->drawAnnotation(rect, key, value.get()); + } + ////////////////////////////////////////////////////////////////////////// +#ifdef SK_INTERNAL +#ifndef SK_SUPPORT_LEGACY_DRAWFILTER + #define SK_SUPPORT_LEGACY_DRAWFILTER +#endif +#endif + +#ifdef SK_SUPPORT_LEGACY_DRAWFILTER /** Get the current filter object. The filter's reference count is not affected. The filter is saved/restored, just like the matrix and clip. @return the canvas' filter (or NULL). */ + SK_ATTR_EXTERNALLY_DEPRECATED("getDrawFilter use is deprecated") SkDrawFilter* getDrawFilter() const; /** Set the new filter (or NULL). Pass NULL to clear any existing filter. @@ -1164,8 +1175,9 @@ public: @param filter the new filter (or NULL) @return the new filter */ + SK_ATTR_EXTERNALLY_DEPRECATED("setDrawFilter use is deprecated") virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter); - +#endif ////////////////////////////////////////////////////////////////////////// /** @@ -1265,7 +1277,7 @@ public: protected: // default impl defers to getDevice()->newSurface(info) - virtual SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&); + virtual sk_sp onNewSurface(const SkImageInfo&, const SkSurfaceProps&); // default impl defers to its device virtual bool onPeekPixels(SkPixmap*); @@ -1289,6 +1301,7 @@ protected: virtual void didConcat(const SkMatrix&) {} virtual void didSetMatrix(const SkMatrix&) {} + virtual void onDrawAnnotation(const SkRect&, const char key[], SkData* value); virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&); virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, @@ -1364,16 +1377,8 @@ protected: bool clipRectBounds(const SkRect* bounds, SaveLayerFlags, SkIRect* intersection, const SkImageFilter* imageFilter = NULL); -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS - // Needed by SkiaCanvasProxy in Android. Make sure that class is updated - // before removing this method. - static uint32_t SaveLayerFlagsToSaveFlags(SaveLayerFlags); -#endif private: static bool BoundsAffectsClip(SaveLayerFlags); -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS - static uint32_t SaveFlagsToSaveLayerFlags(SaveFlags); -#endif static SaveLayerFlags LegacySaveFlagsToSaveLayerFlags(uint32_t legacySaveFlags); enum ShaderOverrideOpacity { @@ -1457,11 +1462,10 @@ private: SkBaseDevice* init(SkBaseDevice*, InitFlags); /** - * Gets the size/origin of the top level layer in global canvas coordinates. We don't want this + * Gets the bounds of the top level layer in global canvas coordinates. We don't want this * to be public because it exposes decisions about layer sizes that are internal to the canvas. */ - SkISize getTopLayerSize() const; - SkIPoint getTopLayerOrigin() const; + SkIRect getTopLayerBounds() const; void internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint* paint, @@ -1570,19 +1574,6 @@ private: }; #define SkAutoCanvasRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoCanvasRestore) -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS -static inline SkCanvas::SaveFlags operator|(const SkCanvas::SaveFlags lhs, - const SkCanvas::SaveFlags rhs) { - return static_cast(static_cast(lhs) | static_cast(rhs)); -} - -static inline SkCanvas::SaveFlags& operator|=(SkCanvas::SaveFlags& lhs, - const SkCanvas::SaveFlags rhs) { - lhs = lhs | rhs; - return lhs; -} -#endif - class SkCanvasClipVisitor { public: virtual ~SkCanvasClipVisitor(); diff --git a/gfx/skia/skia/include/core/SkClipStack.h b/gfx/skia/skia/include/core/SkClipStack.h index b74e47697f..973f70a42b 100644 --- a/gfx/skia/skia/include/core/SkClipStack.h +++ b/gfx/skia/skia/include/core/SkClipStack.h @@ -13,7 +13,6 @@ #include "SkRect.h" #include "SkRRect.h" #include "SkRegion.h" -#include "SkTDArray.h" #include "SkTLazy.h" class SkCanvasClipVisitor; @@ -99,6 +98,9 @@ public: //!< Call to get the element as a path, regardless of its type. void asPath(SkPath* path) const; + //!< Call if getType() is not kPath to get the element as a round rect. + const SkRRect& asRRect() const { SkASSERT(kPath_Type != fType); return fRRect; } + /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased when it is rasterized. */ bool isAA() const { return fDoAA; } diff --git a/gfx/skia/skia/include/core/SkColor.h b/gfx/skia/skia/include/core/SkColor.h index 1ba1331c1a..a40e5f1f15 100644 --- a/gfx/skia/skia/include/core/SkColor.h +++ b/gfx/skia/skia/include/core/SkColor.h @@ -110,8 +110,7 @@ SK_API void SkRGBToHSV(U8CPU red, U8CPU green, U8CPU blue, SkScalar hsv[3]); @param color the argb color to convert. Note: the alpha component is ignored. @param hsv 3 element array which holds the resulting HSV components. */ -static inline void SkColorToHSV(SkColor color, SkScalar hsv[3]) -{ +static inline void SkColorToHSV(SkColor color, SkScalar hsv[3]) { SkRGBToHSV(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color), hsv); } @@ -134,8 +133,7 @@ SK_API SkColor SkHSVToColor(U8CPU alpha, const SkScalar hsv[3]); @param hsv 3 element array which holds the input HSV components. @return the resulting argb color */ -static inline SkColor SkHSVToColor(const SkScalar hsv[3]) -{ +static inline SkColor SkHSVToColor(const SkScalar hsv[3]) { return SkHSVToColor(0xFF, hsv); } @@ -160,4 +158,37 @@ SK_API SkPMColor SkPreMultiplyColor(SkColor c); */ typedef SkPMColor (*SkXfermodeProc)(SkPMColor src, SkPMColor dst); +/////////////////////////////////////////////////////////////////////////////////////////////////// + +struct SkPM4f; + +/* + * The float values are 0...1 unpremultiplied + */ +struct SkColor4f { + float fA; + float fR; + float fG; + float fB; + + bool operator==(const SkColor4f& other) const { + return fA == other.fA && fR == other.fR && fG == other.fG && fB == other.fB; + } + bool operator!=(const SkColor4f& other) const { + return !(*this == other); + } + + const float* vec() const { return &fA; } + float* vec() { return &fA; } + + static SkColor4f Pin(float a, float r, float g, float b); + static SkColor4f FromColor(SkColor); + + SkColor4f pin() const { + return Pin(fA, fR, fG, fB); + } + + SkPM4f premul() const; +}; + #endif diff --git a/gfx/skia/skia/include/core/SkColorFilter.h b/gfx/skia/skia/include/core/SkColorFilter.h index c5d084a22d..7ac335fb10 100644 --- a/gfx/skia/skia/include/core/SkColorFilter.h +++ b/gfx/skia/skia/include/core/SkColorFilter.h @@ -10,7 +10,6 @@ #include "SkColor.h" #include "SkFlattenable.h" -#include "SkTDArray.h" #include "SkXfermode.h" class GrContext; @@ -68,10 +67,12 @@ public: */ virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) const = 0; + virtual void filterSpan4f(const SkPM4f src[], int count, SkPM4f result[]) const; + enum Flags { /** If set the filter methods will not change the alpha channel of the colors. */ - kAlphaUnchanged_Flag = 0x01, + kAlphaUnchanged_Flag = 1 << 0, }; /** Returns the flags for this filter. Override in subclasses to return custom flags. @@ -85,7 +86,7 @@ public: * * e.g. result(color) == this_filter(inner(color)) */ - virtual SkColorFilter* newComposed(const SkColorFilter* /*inner*/) const { return NULL; } + virtual sk_sp makeComposed(sk_sp) const { return nullptr; } /** * Apply this colorfilter to the specified SkColor. This routine handles @@ -95,6 +96,11 @@ public: */ SkColor filterColor(SkColor) const; + /** + * Filters a single color. + */ + SkColor4f filterColor4f(const SkColor4f&) const; + /** Create a colorfilter that uses the specified color and mode. If the Mode is DST, this function will return NULL (since that mode will have no effect on the result). @@ -104,7 +110,7 @@ public: @return colorfilter object that applies the src color and mode, or NULL if the mode will have no effect. */ - static SkColorFilter* CreateModeFilter(SkColor c, SkXfermode::Mode mode); + static sk_sp MakeModeFilter(SkColor c, SkXfermode::Mode mode); /** Construct a colorfilter whose effect is to first apply the inner filter and then apply * the outer filter to the result of the inner's. @@ -113,7 +119,28 @@ public: * Due to internal limits, it is possible that this will return NULL, so the caller must * always check. */ - static SkColorFilter* CreateComposeFilter(SkColorFilter* outer, SkColorFilter* inner); + static sk_sp MakeComposeFilter(sk_sp outer, + sk_sp inner); + + /** Construct a color filter that transforms a color by a 4x5 matrix. The matrix is in row- + * major order and the translation column is specified in unnormalized, 0...255, space. + */ + static sk_sp MakeMatrixFilterRowMajor255(const SkScalar array[20]); + +#ifdef SK_SUPPORT_LEGACY_COLORFILTER_PTR + static SkColorFilter* CreateModeFilter(SkColor c, SkXfermode::Mode mode) { + return MakeModeFilter(c, mode).release(); + } + static SkColorFilter* CreateComposeFilter(SkColorFilter* outer, SkColorFilter* inner) { + return MakeComposeFilter(sk_ref_sp(outer), sk_ref_sp(inner)).release(); + } + static SkColorFilter* CreateMatrixFilterRowMajor255(const SkScalar array[20]) { + return MakeMatrixFilterRowMajor255(array).release(); + } + virtual SkColorFilter* newComposed(const SkColorFilter* inner) const { + return this->makeComposed(sk_ref_sp(const_cast(inner))).release(); + } +#endif /** * A subclass may implement this factory function to work with the GPU backend. It returns diff --git a/gfx/skia/skia/include/core/SkColorPriv.h b/gfx/skia/skia/include/core/SkColorPriv.h index 6347660dbc..40e6e15dc8 100644 --- a/gfx/skia/skia/include/core/SkColorPriv.h +++ b/gfx/skia/skia/include/core/SkColorPriv.h @@ -71,6 +71,11 @@ SK_B32_SHIFT == SK_BGRA_B32_SHIFT) +#define SK_A_INDEX (SK_A32_SHIFT/8) +#define SK_R_INDEX (SK_R32_SHIFT/8) +#define SK_G_INDEX (SK_G32_SHIFT/8) +#define SK_B_INDEX (SK_B32_SHIFT/8) + #if defined(SK_PMCOLOR_IS_RGBA) && !LOCAL_PMCOLOR_SHIFTS_EQUIVALENT_TO_RGBA #error "SK_PMCOLOR_IS_RGBA does not match SK_*32_SHIFT values" #endif @@ -219,13 +224,7 @@ static inline int SkAlphaBlend255(S16CPU src, S16CPU dst, U8CPU alpha) { } static inline U8CPU SkUnitScalarClampToByte(SkScalar x) { - if (x < 0) { - return 0; - } - if (x >= SK_Scalar1) { - return 255; - } - return SkScalarToFixed(x) >> 8; + return static_cast(SkScalarPin(x, 0, 1) * 255 + 0.5); } #define SK_R16_BITS 5 diff --git a/gfx/skia/skia/include/core/SkData.h b/gfx/skia/skia/include/core/SkData.h index 60a98e00f0..628029f82b 100644 --- a/gfx/skia/skia/include/core/SkData.h +++ b/gfx/skia/skia/include/core/SkData.h @@ -14,6 +14,8 @@ class SkStream; +#define SK_SUPPORT_LEGACY_DATA_FACTORIES + /** * SkData holds an immutable data buffer. Not only is the data immutable, * but the actual ptr that is returned (by data() or bytes()) is guaranteed @@ -67,6 +69,7 @@ public: * effectively returning 0 == memcmp(...) */ bool equals(const SkData* other) const; + bool equals(sk_sp& other) const { return this->equals(other.get()); } /** * Function that, if provided, will be called when the SkData goes out @@ -77,13 +80,14 @@ public: /** * Create a new dataref by copying the specified data */ - static SkData* NewWithCopy(const void* data, size_t length); + static sk_sp MakeWithCopy(const void* data, size_t length); + /** * Create a new data with uninitialized contents. The caller should call writable_data() * to write into the buffer, but this must be done before another ref() is made. */ - static SkData* NewUninitialized(size_t length); + static sk_sp MakeUninitialized(size_t length); /** * Create a new dataref by copying the specified c-string @@ -91,33 +95,33 @@ public: * equal to strlen(cstr) + 1. If cstr is NULL, it will be treated the same * as "". */ - static SkData* NewWithCString(const char cstr[]); + static sk_sp MakeWithCString(const char cstr[]); /** * Create a new dataref, taking the ptr as is, and using the * releaseproc to free it. The proc may be NULL. */ - static SkData* NewWithProc(const void* ptr, size_t length, ReleaseProc proc, void* context); + static sk_sp MakeWithProc(const void* ptr, size_t length, ReleaseProc proc, void* ctx); /** * Call this when the data parameter is already const and will outlive the lifetime of the * SkData. Suitable for with const globals. */ - static SkData* NewWithoutCopy(const void* data, size_t length) { - return NewWithProc(data, length, DummyReleaseProc, NULL); + static sk_sp MakeWithoutCopy(const void* data, size_t length) { + return MakeWithProc(data, length, DummyReleaseProc, nullptr); } /** * Create a new dataref from a pointer allocated by malloc. The Data object * takes ownership of that allocation, and will handling calling sk_free. */ - static SkData* NewFromMalloc(const void* data, size_t length); + static sk_sp MakeFromMalloc(const void* data, size_t length); /** * Create a new dataref the file with the specified path. * If the file cannot be opened, this returns NULL. */ - static SkData* NewFromFileName(const char path[]); + static sk_sp MakeFromFileName(const char path[]); /** * Create a new dataref from a stdio FILE. @@ -126,7 +130,7 @@ public: * The FILE must be open for reading only. * Returns NULL on failure. */ - static SkData* NewFromFILE(FILE* f); + static sk_sp MakeFromFILE(FILE* f); /** * Create a new dataref from a file descriptor. @@ -135,26 +139,57 @@ public: * The file descriptor must be open for reading only. * Returns NULL on failure. */ - static SkData* NewFromFD(int fd); + static sk_sp MakeFromFD(int fd); /** * Attempt to read size bytes into a SkData. If the read succeeds, return the data, * else return NULL. Either way the stream's cursor may have been changed as a result * of calling read(). */ - static SkData* NewFromStream(SkStream*, size_t size); + static sk_sp MakeFromStream(SkStream*, size_t size); /** * Create a new dataref using a subset of the data in the specified * src dataref. */ - static SkData* NewSubset(const SkData* src, size_t offset, size_t length); + static sk_sp MakeSubset(const SkData* src, size_t offset, size_t length); /** * Returns a new empty dataref (or a reference to a shared empty dataref). * New or shared, the caller must see that unref() is eventually called. */ - static SkData* NewEmpty(); + static sk_sp MakeEmpty(); + +#ifdef SK_SUPPORT_LEGACY_DATA_FACTORIES + static SkData* NewWithCopy(const void* data, size_t length) { + return MakeWithCopy(data, length).release(); + } + static SkData* NewUninitialized(size_t length) { + return MakeUninitialized(length).release(); + } + static SkData* NewWithCString(const char cstr[]) { + return MakeWithCString(cstr).release(); + } + static SkData* NewWithProc(const void* ptr, size_t length, ReleaseProc proc, void* context) { + return MakeWithProc(ptr, length, proc, context).release(); + } + static SkData* NewWithoutCopy(const void* data, size_t length) { + return MakeWithoutCopy(data, length).release(); + } + static SkData* NewFromMalloc(const void* data, size_t length) { + return MakeFromMalloc(data, length).release(); + } + static SkData* NewFromFileName(const char path[]) { return MakeFromFileName(path).release(); } + static SkData* NewFromFILE(FILE* f) { return MakeFromFILE(f).release(); } + static SkData* NewFromFD(int fd) { return MakeFromFD(fd).release(); } + static SkData* NewFromStream(SkStream* stream, size_t size) { + return MakeFromStream(stream, size).release(); + } + static SkData* NewSubset(const SkData* src, size_t offset, size_t length) { + return MakeSubset(src, offset, length).release(); + } + static SkData* NewEmpty() { return MakeEmpty().release(); } +#endif private: ReleaseProc fReleaseProc; @@ -179,14 +214,16 @@ private: friend SkData* sk_new_empty_data(); // shared internal factory - static SkData* PrivateNewWithCopy(const void* srcOrNull, size_t length); + static sk_sp PrivateNewWithCopy(const void* srcOrNull, size_t length); static void DummyReleaseProc(const void*, void*) {} typedef SkRefCnt INHERITED; }; +#ifdef SK_SUPPORT_LEGACY_DATA_FACTORIES /** Typedef of SkAutoTUnref for automatically unref-ing a SkData. */ typedef SkAutoTUnref SkAutoDataUnref; +#endif #endif diff --git a/gfx/skia/skia/include/core/SkDataTable.h b/gfx/skia/skia/include/core/SkDataTable.h index 798ca9c0c4..c9d915d255 100644 --- a/gfx/skia/skia/include/core/SkDataTable.h +++ b/gfx/skia/skia/include/core/SkDataTable.h @@ -8,10 +8,10 @@ #ifndef SkDataTable_DEFINED #define SkDataTable_DEFINED +#include "../private/SkTDArray.h" #include "SkChunkAlloc.h" #include "SkData.h" #include "SkString.h" -#include "SkTDArray.h" /** * Like SkData, SkDataTable holds an immutable data buffer. The data buffer is diff --git a/gfx/skia/skia/include/core/SkDevice.h b/gfx/skia/skia/include/core/SkDevice.h index 65ec56f902..c15aeccf82 100644 --- a/gfx/skia/skia/include/core/SkDevice.h +++ b/gfx/skia/skia/include/core/SkDevice.h @@ -40,6 +40,13 @@ public: */ virtual SkImageInfo imageInfo() const; + /** + * Return SurfaceProps for this device. + */ + const SkSurfaceProps& surfaceProps() const { + return fSurfaceProps; + } + /** * Return the bounds of the device in the coordinate space of the root * canvas. The root device will have its top-left at 0,0, but other devices @@ -252,6 +259,8 @@ protected: virtual void drawAtlas(const SkDraw&, const SkImage* atlas, const SkRSXform[], const SkRect[], const SkColor[], int count, SkXfermode::Mode, const SkPaint&); + virtual void drawAnnotation(const SkDraw&, const SkRect&, const char[], SkData*) {} + /** The SkDevice passed will be an SkDevice which was returned by a call to onCreateDevice on this device with kNeverTile_TileExpectation. */ @@ -294,7 +303,7 @@ protected: } protected: - virtual SkSurface* newSurface(const SkImageInfo&, const SkSurfaceProps&) { return NULL; } + virtual sk_sp makeSurface(const SkImageInfo&, const SkSurfaceProps&); virtual bool onPeekPixels(SkPixmap*) { return false; } /** @@ -315,10 +324,6 @@ protected: virtual bool onAccessPixels(SkPixmap*) { return false; } - const SkSurfaceProps& surfaceProps() const { - return fSurfaceProps; - } - /** * PRIVATE / EXPERIMENTAL -- do not call * This entry point gives the backend an opportunity to take over the rendering @@ -376,6 +381,12 @@ protected: return NULL; } + /** + * Calls through to drawSprite, processing the imagefilter. + */ + virtual void drawSpriteWithFilter(const SkDraw&, const SkBitmap&, + int x, int y, const SkPaint&); + private: friend class SkCanvas; friend struct DeviceCM; //for setMatrixClip @@ -386,11 +397,6 @@ private: friend class SkNoPixelsBitmapDevice; friend class SkSurface_Raster; - /** - * Calls through to drawSprite, processing imagefilter as needed. - */ - void drawBitmapAsSprite(const SkDraw&, const SkBitmap&, int x, int y, const SkPaint&); - // used to change the backend's pixels (and possibly config/rowbytes) // but cannot change the width/height, so there should be no change to // any clip information. diff --git a/gfx/skia/skia/include/core/SkDocument.h b/gfx/skia/skia/include/core/SkDocument.h index 6ee96b9ce3..e5d8cf3a9b 100644 --- a/gfx/skia/skia/include/core/SkDocument.h +++ b/gfx/skia/skia/include/core/SkDocument.h @@ -131,7 +131,7 @@ public: * nullptr. For example: * * SkDocument* make_doc(SkWStream* output) { - * SkTArray info; + * std::vector info; * info.emplace_back(SkString("Title"), SkString("...")); * info.emplace_back(SkString("Author"), SkString("...")); * info.emplace_back(SkString("Subject"), SkString("...")); @@ -140,7 +140,7 @@ public: * SkTime::DateTime now; * SkTime::GetDateTime(&now); * SkDocument* doc = SkDocument::CreatePDF(output); - * doc->setMetadata(info, &now, &now); + * doc->setMetadata(&info[0], (int)info.size(), &now, &now); * return doc; * } */ @@ -148,7 +148,8 @@ public: SkString fKey, fValue; Attribute(const SkString& k, const SkString& v) : fKey(k), fValue(v) {} }; - virtual void setMetadata(const SkTArray&, + virtual void setMetadata(const SkDocument::Attribute[], + int /* attributeCount */, const SkTime::DateTime* /* creationDate */, const SkTime::DateTime* /* modifiedDate */) {} diff --git a/gfx/skia/skia/include/core/SkDraw.h b/gfx/skia/skia/include/core/SkDraw.h index b8cf8027dd..ea8638a910 100644 --- a/gfx/skia/skia/include/core/SkDraw.h +++ b/gfx/skia/skia/include/core/SkDraw.h @@ -119,7 +119,7 @@ public: void drawPosText_asPaths(const char text[], size_t byteLength, const SkScalar pos[], int scalarsPerPosition, const SkPoint& offset, const SkPaint&) const; - + static SkScalar ComputeResScaleForStroking(const SkMatrix& ); private: void drawDevMask(const SkMask& mask, const SkPaint&) const; void drawBitmapAsMask(const SkBitmap&, const SkPaint&) const; @@ -139,6 +139,9 @@ private: bool SK_WARN_UNUSED_RESULT computeConservativeLocalClipBounds(SkRect* bounds) const; + /** Returns the current setting for using fake gamma. */ + SkPaint::FakeGamma SK_WARN_UNUSED_RESULT fakeGamma() const; + public: SkPixmap fDst; const SkMatrix* fMatrix; // required diff --git a/gfx/skia/skia/include/core/SkFlattenable.h b/gfx/skia/skia/include/core/SkFlattenable.h index 10cba1a2ec..c76f119c13 100644 --- a/gfx/skia/skia/include/core/SkFlattenable.h +++ b/gfx/skia/skia/include/core/SkFlattenable.h @@ -48,7 +48,7 @@ class SkPrivateEffectInitializer; #define SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(flattenable) \ private: \ - static SkFlattenable* CreateProc(SkReadBuffer&); \ + static sk_sp CreateProc(SkReadBuffer&); \ friend class SkFlattenable::PrivateInitializer; \ public: \ Factory getFactory() const override { return CreateProc; } @@ -82,7 +82,7 @@ public: kSkXfermode_Type, }; - typedef SkFlattenable* (*Factory)(SkReadBuffer&); + typedef sk_sp (*Factory)(SkReadBuffer&); SkFlattenable() {} diff --git a/gfx/skia/skia/include/core/SkGraphics.h b/gfx/skia/skia/include/core/SkGraphics.h index 5aecc7acd0..d5a730d9e0 100644 --- a/gfx/skia/skia/include/core/SkGraphics.h +++ b/gfx/skia/skia/include/core/SkGraphics.h @@ -119,6 +119,14 @@ public: */ static void DumpMemoryStatistics(SkTraceMemoryDump* dump); + /** + * Free as much globally cached memory as possible. This will purge all private caches in Skia, + * including font and image caches. + * + * If there are caches associated with GPU context, those will not be affected by this call. + */ + static void PurgeAllCaches(); + /** * Applications with command line options may pass optional state, such * as cache sizes, here, for instance: diff --git a/gfx/skia/skia/include/core/SkImage.h b/gfx/skia/skia/include/core/SkImage.h index 62a9e9cf42..6be3b6e54a 100644 --- a/gfx/skia/skia/include/core/SkImage.h +++ b/gfx/skia/skia/include/core/SkImage.h @@ -25,8 +25,11 @@ class SkPixelSerializer; class SkString; class SkSurface; class GrContext; +class GrContextThreadSafeProxy; class GrTexture; +#define SK_SUPPORT_LEGACY_IMAGEFACTORY + /** * SkImage is an abstraction for drawing a rectagle of pixels, though the * particular type of image could be actually storing its data on the GPU, or @@ -46,9 +49,8 @@ public: typedef SkImageInfo Info; typedef void* ReleaseContext; - static SkImage* NewRasterCopy(const Info&, const void* pixels, size_t rowBytes, - SkColorTable* ctable = NULL); - static SkImage* NewRasterData(const Info&, SkData* pixels, size_t rowBytes); + static sk_sp MakeRasterCopy(const SkPixmap&); + static sk_sp MakeRasterData(const Info&, sk_sp pixels, size_t rowBytes); typedef void (*RasterReleaseProc)(const void* pixels, ReleaseContext); @@ -57,36 +59,31 @@ public: * until the specified release-proc is called, indicating that Skia no longer has a reference * to the pixels. * - * Returns NULL if the requested Info is unsupported. + * Returns NULL if the requested pixmap info is unsupported. */ - static SkImage* NewFromRaster(const Info&, const void* pixels, size_t rowBytes, - RasterReleaseProc, ReleaseContext); + static sk_sp MakeFromRaster(const SkPixmap&, RasterReleaseProc, ReleaseContext); /** * Construct a new image from the specified bitmap. If the bitmap is marked immutable, and * its pixel memory is shareable, it may be shared instead of copied. */ - static SkImage* NewFromBitmap(const SkBitmap&); + static sk_sp MakeFromBitmap(const SkBitmap&); /** - * Construct a new SkImage based on the given ImageGenerator. - * This function will always take ownership of the passed - * ImageGenerator. Returns NULL on error. + * Construct a new SkImage based on the given ImageGenerator. Returns NULL on error. + * This function will always take ownership of the passed generator. * * If a subset is specified, it must be contained within the generator's bounds. */ - static SkImage* NewFromGenerator(SkImageGenerator*, const SkIRect* subset = NULL); + static sk_sp MakeFromGenerator(SkImageGenerator*, const SkIRect* subset = NULL); /** * Construct a new SkImage based on the specified encoded data. Returns NULL on failure, * which can mean that the format of the encoded data was not recognized/supported. * * If a subset is specified, it must be contained within the encoded data's bounds. - * - * Regardless of success or failure, the caller is responsible for managing their ownership - * of the data. */ - static SkImage* NewFromEncoded(SkData* encoded, const SkIRect* subset = NULL); + static sk_sp MakeFromEncoded(sk_sp encoded, const SkIRect* subset = NULL); /** * Create a new image from the specified descriptor. Note - the caller is responsible for @@ -94,12 +91,13 @@ public: * * Will return NULL if the specified descriptor is unsupported. */ - static SkImage* NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc) { - return NewFromTexture(ctx, desc, kPremul_SkAlphaType, NULL, NULL); + static sk_sp MakeFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc) { + return MakeFromTexture(ctx, desc, kPremul_SkAlphaType, NULL, NULL); } - static SkImage* NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& de, SkAlphaType at) { - return NewFromTexture(ctx, de, at, NULL, NULL); + static sk_sp MakeFromTexture(GrContext* ctx, const GrBackendTextureDesc& de, + SkAlphaType at) { + return MakeFromTexture(ctx, de, at, NULL, NULL); } typedef void (*TextureReleaseProc)(ReleaseContext); @@ -111,8 +109,8 @@ public: * * Will return NULL if the specified descriptor is unsupported. */ - static SkImage* NewFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType, - TextureReleaseProc, ReleaseContext); + static sk_sp MakeFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType, + TextureReleaseProc, ReleaseContext); /** * Create a new image from the specified descriptor. Note - Skia will delete or recycle the @@ -120,8 +118,8 @@ public: * * Will return NULL if the specified descriptor is unsupported. */ - static SkImage* NewFromAdoptedTexture(GrContext*, const GrBackendTextureDesc&, - SkAlphaType = kPremul_SkAlphaType); + static sk_sp MakeFromAdoptedTexture(GrContext*, const GrBackendTextureDesc&, + SkAlphaType = kPremul_SkAlphaType); /** * Create a new image by copying the pixels from the specified descriptor. No reference is @@ -129,21 +127,23 @@ public: * * Will return NULL if the specified descriptor is unsupported. */ - static SkImage* NewFromTextureCopy(GrContext*, const GrBackendTextureDesc&, - SkAlphaType = kPremul_SkAlphaType); + static sk_sp MakeFromTextureCopy(GrContext*, const GrBackendTextureDesc&, + SkAlphaType = kPremul_SkAlphaType); /** * Create a new image by copying the pixels from the specified y, u, v textures. The data * from the textures is immediately ingested into the image and the textures can be modified or * deleted after the function returns. The image will have the dimensions of the y texture. */ - static SkImage* NewFromYUVTexturesCopy(GrContext*, SkYUVColorSpace, - const GrBackendObject yuvTextureHandles[3], - const SkISize yuvSizes[3], - GrSurfaceOrigin); + static sk_sp MakeFromYUVTexturesCopy(GrContext*, SkYUVColorSpace, + const GrBackendObject yuvTextureHandles[3], + const SkISize yuvSizes[3], + GrSurfaceOrigin); - static SkImage* NewFromPicture(const SkPicture*, const SkISize& dimensions, - const SkMatrix*, const SkPaint*); + static sk_sp MakeFromPicture(sk_sp, const SkISize& dimensions, + const SkMatrix*, const SkPaint*); + + static sk_sp MakeTextureFromPixmap(GrContext*, const SkPixmap&, SkBudgeted budgeted); /////////////////////////////////////////////////////////////////////////////////////////////// @@ -154,10 +154,31 @@ public: uint32_t uniqueID() const { return fUniqueID; } virtual bool isOpaque() const { return false; } - virtual SkShader* newShader(SkShader::TileMode, - SkShader::TileMode, - const SkMatrix* localMatrix = NULL) const; + /** + * Extracts YUV planes from the SkImage and stores them in client-provided memory. The sizes + * planes and rowBytes arrays are ordered [y, u, v]. + */ + bool readYUV8Planes(const SkISize[3], void* const planes[3], const size_t rowBytes[3], + SkYUVColorSpace) const; +#ifdef SK_SUPPORT_LEGACY_CREATESHADER_PTR + SkShader* newShader(SkShader::TileMode, SkShader::TileMode, + const SkMatrix* localMatrix = nullptr) const; +#endif + + sk_sp makeShader(SkShader::TileMode, SkShader::TileMode, + const SkMatrix* localMatrix = nullptr) const; + + /** + * If the image has direct access to its pixels (i.e. they are in local RAM) + * return true, and if not null, return in the pixmap parameter the info about the + * images pixels. + * + * On failure, return false and ignore the pixmap parameter. + */ + bool peekPixels(SkPixmap* pixmap) const; + +#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS /** * If the image has direct access to its pixels (i.e. they are in local * RAM) return the (const) address of those pixels, and if not null, return @@ -166,17 +187,11 @@ public: * * On failure, returns NULL and the info and rowBytes parameters are * ignored. + * + * DEPRECATED -- use the SkPixmap variant instead */ const void* peekPixels(SkImageInfo* info, size_t* rowBytes) const; - - /** - * If the image has direct access to its pixels (i.e. they are in local - * RAM) return the (const) address of those pixels, and if not null, return - * true, and if pixmap is not NULL, set it to point into the image. - * - * On failure, return false and ignore the pixmap parameter. - */ - bool peekPixels(SkPixmap* pixmap) const; +#endif /** * Some images have to perform preliminary work in preparation for drawing. This can be @@ -192,7 +207,7 @@ public: */ void preroll(GrContext* = nullptr) const; - // DEPRECATED + // DEPRECATED - currently used by Canvas2DLayerBridge in Chromium. GrTexture* getTexture() const; /** @@ -298,7 +313,52 @@ public: * If subset does not intersect the bounds of this image, or the copy/share cannot be made, * NULL will be returned. */ - SkImage* newSubset(const SkIRect& subset) const; + sk_sp makeSubset(const SkIRect& subset) const; + + /** + * Ensures that an image is backed by a texture (when GrContext is non-null). If no + * transformation is required, the returned image may be the same as this image. If the this + * image is from a different GrContext, this will fail. + */ + sk_sp makeTextureImage(GrContext*) const; + + /** Drawing params for which a deferred texture image data should be optimized. */ + struct DeferredTextureImageUsageParams { + SkMatrix fMatrix; + SkFilterQuality fQuality; + }; + + /** + * This method allows clients to capture the data necessary to turn a SkImage into a texture- + * backed image. If the original image is codec-backed this will decode into a format optimized + * for the context represented by the proxy. This method is thread safe with respect to the + * GrContext whence the proxy came. Clients allocate and manage the storage of the deferred + * texture data and control its lifetime. No cleanup is required, thus it is safe to simply free + * the memory out from under the data. + * + * The same method is used both for getting the size necessary for pre-uploaded texture data + * and for retrieving the data. The params array represents the set of draws over which to + * optimize the pre-upload data. + * + * When called with a null buffer this returns the size that the client must allocate in order + * to create deferred texture data for this image (or zero if this is an inappropriate + * candidate). The buffer allocated by the client should be 8 byte aligned. + * + * When buffer is not null this fills in the deferred texture data for this image in the + * provided buffer (assuming this is an appropriate candidate image and the buffer is + * appropriately aligned). Upon success the size written is returned, otherwise 0. + */ + size_t getDeferredTextureImageData(const GrContextThreadSafeProxy&, + const DeferredTextureImageUsageParams[], + int paramCnt, + void* buffer) const; + + /** + * Returns a texture-backed image from data produced in SkImage::getDeferredTextureImageData. + * The context must be the context that provided the proxy passed to + * getDeferredTextureImageData. + */ + static sk_sp MakeFromDeferredTextureImageData(GrContext*, const void*, SkBudgeted); // Helper functions to convert to SkBitmap @@ -324,6 +384,42 @@ public: */ bool isLazyGenerated() const; + +#ifdef SK_SUPPORT_LEGACY_IMAGEFACTORY + static SkImage* NewRasterCopy(const Info&, const void* pixels, size_t rowBytes, + SkColorTable* ctable = nullptr); + static SkImage* NewRasterData(const Info&, SkData* pixels, size_t rowBytes); + static SkImage* NewFromRaster(const Info&, const void* pixels, size_t rowBytes, + RasterReleaseProc, ReleaseContext); + static SkImage* NewFromBitmap(const SkBitmap&); + static SkImage* NewFromGenerator(SkImageGenerator*, const SkIRect* subset = NULL); + static SkImage* NewFromEncoded(SkData* encoded, const SkIRect* subset = NULL); + static SkImage* NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc) { + return NewFromTexture(ctx, desc, kPremul_SkAlphaType, NULL, NULL); + } + + static SkImage* NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& de, SkAlphaType at) { + return NewFromTexture(ctx, de, at, NULL, NULL); + } + static SkImage* NewFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType, + TextureReleaseProc, ReleaseContext); + static SkImage* NewFromAdoptedTexture(GrContext*, const GrBackendTextureDesc&, + SkAlphaType = kPremul_SkAlphaType); + static SkImage* NewFromTextureCopy(GrContext*, const GrBackendTextureDesc&, + SkAlphaType = kPremul_SkAlphaType); + static SkImage* NewFromYUVTexturesCopy(GrContext*, SkYUVColorSpace, + const GrBackendObject yuvTextureHandles[3], + const SkISize yuvSizes[3], + GrSurfaceOrigin); + static SkImage* NewFromPicture(const SkPicture*, const SkISize& dimensions, + const SkMatrix*, const SkPaint*); + static SkImage* NewTextureFromPixmap(GrContext*, const SkPixmap&, SkBudgeted budgeted); + static SkImage* NewFromDeferredTextureImageData(GrContext*, const void*, SkBudgeted); + + SkImage* newSubset(const SkIRect& subset) const { return this->makeSubset(subset).release(); } + SkImage* newTextureImage(GrContext* ctx) const { return this->makeTextureImage(ctx).release(); } +#endif + protected: SkImage(int width, int height, uint32_t uniqueID); diff --git a/gfx/skia/skia/include/core/SkImageDecoder.h b/gfx/skia/skia/include/core/SkImageDecoder.h deleted file mode 100644 index 5060a1e599..0000000000 --- a/gfx/skia/skia/include/core/SkImageDecoder.h +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkImageDecoder_DEFINED -#define SkImageDecoder_DEFINED - -#include "SkBitmap.h" -#include "SkImage.h" -#include "SkPngChunkReader.h" -#include "SkRect.h" -#include "SkRefCnt.h" -#include "SkTRegistry.h" -#include "SkTypes.h" - -class SkStream; -class SkStreamRewindable; - -/** \class SkImageDecoder - - Base class for decoding compressed images into a SkBitmap -*/ -class SkImageDecoder : SkNoncopyable { -public: - virtual ~SkImageDecoder(); - - // TODO (scroggo): Merge with SkEncodedFormat - enum Format { - kUnknown_Format, - kBMP_Format, - kGIF_Format, - kICO_Format, - kJPEG_Format, - kPNG_Format, - kWBMP_Format, - kWEBP_Format, - kPKM_Format, - kKTX_Format, - kASTC_Format, - - kLastKnownFormat = kKTX_Format, - }; - - /** Return the format of image this decoder can decode. If this decoder can decode multiple - formats, kUnknown_Format will be returned. - */ - virtual Format getFormat() const; - - /** If planes or rowBytes is NULL, decodes the header and computes componentSizes - for memory allocation. - Otherwise, decodes the YUV planes into the provided image planes and - updates componentSizes to the final image size. - Returns whether the decoding was successful. - */ - bool decodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], void* planes[3], - size_t rowBytes[3], SkYUVColorSpace*); - - /** Return the format of the SkStreamRewindable or kUnknown_Format if it cannot be determined. - Rewinds the stream before returning. - */ - static Format GetStreamFormat(SkStreamRewindable*); - - /** Return a readable string of the Format provided. - */ - static const char* GetFormatName(Format); - - /** Return a readable string of the value returned by getFormat(). - */ - const char* getFormatName() const; - - /** Whether the decoder should skip writing zeroes to output if possible. - */ - bool getSkipWritingZeroes() const { return fSkipWritingZeroes; } - - /** Set to true if the decoder should skip writing any zeroes when - creating the output image. - This is a hint that may not be respected by the decoder. - It should only be used if it is known that the memory to write - to has already been set to 0; otherwise the resulting image will - have garbage. - This is ideal for images that contain a lot of completely transparent - pixels, but may be a performance hit for an image that has only a - few transparent pixels. - The default is false. - */ - void setSkipWritingZeroes(bool skip) { fSkipWritingZeroes = skip; } - - /** Returns true if the decoder should try to dither the resulting image. - The default setting is true. - */ - bool getDitherImage() const { return fDitherImage; } - - /** Set to true if the the decoder should try to dither the resulting image. - The default setting is true. - */ - void setDitherImage(bool dither) { fDitherImage = dither; } - - /** Returns true if the decoder should try to decode the - resulting image to a higher quality even at the expense of - the decoding speed. - */ - bool getPreferQualityOverSpeed() const { return fPreferQualityOverSpeed; } - - /** Set to true if the the decoder should try to decode the - resulting image to a higher quality even at the expense of - the decoding speed. - */ - void setPreferQualityOverSpeed(bool qualityOverSpeed) { - fPreferQualityOverSpeed = qualityOverSpeed; - } - - /** Set to true to require the decoder to return a bitmap with unpremultiplied - colors. The default is false, meaning the resulting bitmap will have its - colors premultiplied. - NOTE: Passing true to this function may result in a bitmap which cannot - be properly used by Skia. - */ - void setRequireUnpremultipliedColors(bool request) { - fRequireUnpremultipliedColors = request; - } - - /** Returns true if the decoder will only return bitmaps with unpremultiplied - colors. - */ - bool getRequireUnpremultipliedColors() const { return fRequireUnpremultipliedColors; } - - SkPngChunkReader* getPeeker() const { return fPeeker; } - SkPngChunkReader* setPeeker(SkPngChunkReader*); - - /** - * By default, the codec will try to comply with the "pref" colortype - * that is passed to decode() or decodeSubset(). However, this can be called - * to override that, causing the codec to try to match the src depth instead - * (as shown below). - * - * src_8Index -> kIndex_8_SkColorType - * src_8Gray -> kN32_SkColorType - * src_8bpc -> kN32_SkColorType - */ - void setPreserveSrcDepth(bool preserve) { - fPreserveSrcDepth = preserve; - } - - SkBitmap::Allocator* getAllocator() const { return fAllocator; } - SkBitmap::Allocator* setAllocator(SkBitmap::Allocator*); - - // sample-size, if set to > 1, tells the decoder to return a smaller than - // original bitmap, sampling 1 pixel for every size pixels. e.g. if sample - // size is set to 3, then the returned bitmap will be 1/3 as wide and high, - // and will contain 1/9 as many pixels as the original. - // Note: this is a hint, and the codec may choose to ignore this, or only - // approximate the sample size. - int getSampleSize() const { return fSampleSize; } - void setSampleSize(int size); - - /** Reset the sampleSize to its default of 1 - */ - void resetSampleSize() { this->setSampleSize(1); } - - /** Decoding is synchronous, but for long decodes, a different thread can - call this method safely. This sets a state that the decoders will - periodically check, and if they see it changed to cancel, they will - cancel. This will result in decode() returning false. However, there is - no guarantee that the decoder will see the state change in time, so - it is possible that cancelDecode() will be called, but will be ignored - and decode() will return true (assuming no other problems were - encountered). - - This state is automatically reset at the beginning of decode(). - */ - void cancelDecode() { - // now the subclass must query shouldCancelDecode() to be informed - // of the request - fShouldCancelDecode = true; - } - - /** Passed to the decode method. If kDecodeBounds_Mode is passed, then - only the bitmap's info need be set. If kDecodePixels_Mode - is passed, then the bitmap must have pixels or a pixelRef. - */ - enum Mode { - kDecodeBounds_Mode, //!< only return info in bitmap - kDecodePixels_Mode //!< return entire bitmap (including pixels) - }; - - /** Result of a decode. If read as a boolean, a partial success is - considered a success (true). - */ - enum Result { - kFailure = 0, //!< Image failed to decode. bitmap will be - // unchanged. - kPartialSuccess = 1, //!< Part of the image decoded. The rest is - // filled in automatically - kSuccess = 2 //!< The entire image was decoded, if Mode is - // kDecodePixels_Mode, or the bounds were - // decoded, in kDecodeBounds_Mode. - }; - - /** Given a stream, decode it into the specified bitmap. - If the decoder can decompress the image, it calls bitmap.setInfo(), - and then if the Mode is kDecodePixels_Mode, call allocPixelRef(), - which will allocated a pixelRef. To access the pixel memory, the codec - needs to call lockPixels/unlockPixels on the - bitmap. It can then set the pixels with the decompressed image. - * If the image cannot be decompressed, return kFailure. After the - * decoding, the function converts the decoded colortype in bitmap - * to pref if possible. Whether a conversion is feasible is - * tested by Bitmap::canCopyTo(pref). - - If an SkBitmap::Allocator is installed via setAllocator, it will be - used to allocate the pixel memory. A clever allocator can be used - to allocate the memory from a cache, volatile memory, or even from - an existing bitmap's memory. - - If an SkPngChunkReader is installed via setPeeker, it may be used to - peek into meta data during the decode. - */ - Result decode(SkStream*, SkBitmap* bitmap, SkColorType pref, Mode); - Result decode(SkStream* stream, SkBitmap* bitmap, Mode mode) { - return this->decode(stream, bitmap, kUnknown_SkColorType, mode); - } - - /** Given a stream, this will try to find an appropriate decoder object. - If none is found, the method returns NULL. - */ - static SkImageDecoder* Factory(SkStreamRewindable*); - - /** Decode the image stored in the specified file, and store the result - in bitmap. Return true for success or false on failure. - - @param pref Prefer this colortype. - - @param format On success, if format is non-null, it is set to the format - of the decoded file. On failure it is ignored. - */ - static bool DecodeFile(const char file[], SkBitmap* bitmap, SkColorType pref, Mode, - Format* format = NULL); - static bool DecodeFile(const char file[], SkBitmap* bitmap) { - return DecodeFile(file, bitmap, kUnknown_SkColorType, kDecodePixels_Mode, NULL); - } - - /** Decode the image stored in the specified memory buffer, and store the - result in bitmap. Return true for success or false on failure. - - @param pref Prefer this colortype. - - @param format On success, if format is non-null, it is set to the format - of the decoded buffer. On failure it is ignored. - */ - static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap, SkColorType pref, - Mode, Format* format = NULL); - static bool DecodeMemory(const void* buffer, size_t size, SkBitmap* bitmap){ - return DecodeMemory(buffer, size, bitmap, kUnknown_SkColorType, kDecodePixels_Mode, NULL); - } - - /** Decode the image stored in the specified SkStreamRewindable, and store the result - in bitmap. Return true for success or false on failure. - - @param pref Prefer this colortype. - - @param format On success, if format is non-null, it is set to the format - of the decoded stream. On failure it is ignored. - */ - static bool DecodeStream(SkStreamRewindable* stream, SkBitmap* bitmap, SkColorType pref, Mode, - Format* format = NULL); - static bool DecodeStream(SkStreamRewindable* stream, SkBitmap* bitmap) { - return DecodeStream(stream, bitmap, kUnknown_SkColorType, kDecodePixels_Mode, NULL); - } - -protected: - // must be overridden in subclasses. This guy is called by decode(...) - virtual Result onDecode(SkStream*, SkBitmap* bitmap, Mode) = 0; - - /** If planes or rowBytes is NULL, decodes the header and computes componentSizes - for memory allocation. - Otherwise, decodes the YUV planes into the provided image planes and - updates componentSizes to the final image size. - Returns whether the decoding was successful. - */ - virtual bool onDecodeYUV8Planes(SkStream*, SkISize[3] /*componentSizes*/, - void*[3] /*planes*/, size_t[3] /*rowBytes*/, - SkYUVColorSpace*) { - return false; - } - - /** - * Copy all fields on this decoder to the other decoder. Used by subclasses - * to decode a subimage using a different decoder, but with the same settings. - */ - void copyFieldsToOther(SkImageDecoder* other); - - /** Can be queried from within onDecode, to see if the user (possibly in - a different thread) has requested the decode to cancel. If this returns - true, your onDecode() should stop and return false. - Each subclass needs to decide how often it can query this, to balance - responsiveness with performance. - - Calling this outside of onDecode() may return undefined values. - */ - -public: - bool shouldCancelDecode() const { return fShouldCancelDecode; } - -protected: - SkImageDecoder(); - - /** - * Return the default preference being used by the current or latest call to decode. - */ - SkColorType getDefaultPref() { return fDefaultPref; } - - /* Helper for subclasses. Call this to allocate the pixel memory given the bitmap's info. - Returns true on success. This method handles checking for an optional Allocator. - */ - bool allocPixelRef(SkBitmap*, SkColorTable*) const; - - /** - * The raw data of the src image. - */ - enum SrcDepth { - // Color-indexed. - kIndex_SrcDepth, - // Grayscale in 8 bits. - k8BitGray_SrcDepth, - // 8 bits per component. Used for 24 bit if there is no alpha. - k32Bit_SrcDepth, - }; - /** The subclass, inside onDecode(), calls this to determine the colorType of - the returned bitmap. SrcDepth and hasAlpha reflect the raw data of the - src image. This routine returns the caller's preference given - srcDepth and hasAlpha, or kUnknown_SkColorType if there is no preference. - */ - SkColorType getPrefColorType(SrcDepth, bool hasAlpha) const; - -private: - SkPngChunkReader* fPeeker; - SkBitmap::Allocator* fAllocator; - int fSampleSize; - SkColorType fDefaultPref; // use if fUsePrefTable is false - bool fPreserveSrcDepth; - bool fDitherImage; - bool fSkipWritingZeroes; - mutable bool fShouldCancelDecode; - bool fPreferQualityOverSpeed; - bool fRequireUnpremultipliedColors; -}; - -/** Calling newDecoder with a stream returns a new matching imagedecoder - instance, or NULL if none can be found. The caller must manage its ownership - of the stream as usual, calling unref() when it is done, as the returned - decoder may have called ref() (and if so, the decoder is responsible for - balancing its ownership when it is destroyed). - */ -class SkImageDecoderFactory : public SkRefCnt { -public: - - - virtual SkImageDecoder* newDecoder(SkStreamRewindable*) = 0; - -private: - typedef SkRefCnt INHERITED; -}; - -class SkDefaultImageDecoderFactory : SkImageDecoderFactory { -public: - // calls SkImageDecoder::Factory(stream) - virtual SkImageDecoder* newDecoder(SkStreamRewindable* stream) { - return SkImageDecoder::Factory(stream); - } -}; - -// This macro declares a global (i.e., non-class owned) creation entry point -// for each decoder (e.g., CreateJPEGImageDecoder) -#define DECLARE_DECODER_CREATOR(codec) \ - SkImageDecoder *Create ## codec (); - -// This macro defines the global creation entry point for each decoder. Each -// decoder implementation that registers with the decoder factory must call it. -#define DEFINE_DECODER_CREATOR(codec) \ - SkImageDecoder* Create##codec() { return new Sk##codec; } - -// All the decoders known by Skia. Note that, depending on the compiler settings, -// not all of these will be available -DECLARE_DECODER_CREATOR(BMPImageDecoder); -DECLARE_DECODER_CREATOR(GIFImageDecoder); -DECLARE_DECODER_CREATOR(ICOImageDecoder); -DECLARE_DECODER_CREATOR(JPEGImageDecoder); -DECLARE_DECODER_CREATOR(PNGImageDecoder); -DECLARE_DECODER_CREATOR(WBMPImageDecoder); -DECLARE_DECODER_CREATOR(WEBPImageDecoder); -DECLARE_DECODER_CREATOR(PKMImageDecoder); -DECLARE_DECODER_CREATOR(KTXImageDecoder); -DECLARE_DECODER_CREATOR(ASTCImageDecoder); - -// Typedefs to make registering decoder and formatter callbacks easier. -// These have to be defined outside SkImageDecoder. :( -typedef SkTRegistry SkImageDecoder_DecodeReg; -typedef SkTRegistry SkImageDecoder_FormatReg; - -#endif diff --git a/gfx/skia/skia/include/core/SkImageEncoder.h b/gfx/skia/skia/include/core/SkImageEncoder.h index bb3341f836..1ccfae0bf9 100644 --- a/gfx/skia/skia/include/core/SkImageEncoder.h +++ b/gfx/skia/skia/include/core/SkImageEncoder.h @@ -110,8 +110,12 @@ DECLARE_ENCODER_CREATOR(PNGImageEncoder); DECLARE_ENCODER_CREATOR(KTXImageEncoder); DECLARE_ENCODER_CREATOR(WEBPImageEncoder); -#ifdef SK_BUILD_FOR_IOS -DECLARE_ENCODER_CREATOR(PNGImageEncoder_IOS); +#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) +DECLARE_ENCODER_CREATOR(PNGImageEncoder_CG); +#endif + +#if defined(SK_BUILD_FOR_WIN) +DECLARE_ENCODER_CREATOR(ImageEncoder_WIC); #endif // Typedef to make registering encoder callback easier diff --git a/gfx/skia/skia/include/core/SkImageFilter.h b/gfx/skia/skia/include/core/SkImageFilter.h index 8153bacb79..8a8e512254 100644 --- a/gfx/skia/skia/include/core/SkImageFilter.h +++ b/gfx/skia/skia/include/core/SkImageFilter.h @@ -8,7 +8,9 @@ #ifndef SkImageFilter_DEFINED #define SkImageFilter_DEFINED +#include "../private/SkTArray.h" #include "../private/SkTemplates.h" +#include "../private/SkMutex.h" #include "SkFilterQuality.h" #include "SkFlattenable.h" #include "SkMatrix.h" @@ -21,6 +23,7 @@ class SkBaseDevice; class SkBitmap; class SkColorFilter; struct SkIPoint; +class SkSpecialImage; /** * Base class for image filters. If one is installed in the paint, then @@ -40,9 +43,11 @@ public: static Cache* Create(size_t maxBytes); static Cache* Get(); virtual bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const = 0; + virtual SkSpecialImage* get(const Key& key, SkIPoint* offset) const = 0; virtual void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) = 0; + virtual void set(const Key& key, SkSpecialImage* image, const SkIPoint& offset) = 0; virtual void purge() {} - virtual void purgeByImageFilterId(uint32_t) {} + virtual void purgeByKeys(const Key[], int) {} }; class Context { @@ -83,15 +88,16 @@ public: /** * Apply this cropRect to the imageBounds. If a given edge of the cropRect is not - * set, then the corresponding edge from imageBounds will be used. + * set, then the corresponding edge from imageBounds will be used. If "embiggen" + * is true, the crop rect is allowed to enlarge the size of the rect, otherwise + * it may only reduce the rect. Filters that can affect transparent black should + * pass "true", while all other filters should pass "false". * * Note: imageBounds is in "device" space, as the output cropped rectangle will be, - * so the context's CTM is ignore for those. It is only applied the croprect's bounds. - * - * The resulting rect will be intersected with the context's clip. If that intersection is - * empty, then this returns false and cropped is unmodified. + * so the matrix is ignored for those. It is only applied the croprect's bounds. */ - bool applyTo(const SkIRect& imageBounds, const Context&, SkIRect* cropped) const; + void applyTo(const SkIRect& imageBounds, const SkMatrix&, bool embiggen, + SkIRect* cropped) const; private: SkRect fRect; @@ -135,25 +141,38 @@ public: /** * Request a new (result) image to be created from the src image. - * If the src has no pixels (isNull()) then the request just wants to - * receive the config and width/height of the result. * - * The matrix is the current matrix on the canvas. + * The context contains the environment in which the filter is occurring. + * It includes the clip bounds, CTM and cache. * * Offset is the amount to translate the resulting image relative to the * src when it is drawn. This is an out-param. * - * If the result image cannot be created, return false, in which case both - * the result and offset parameters will be ignored by the caller. + * If the result image cannot be created, return null, in which case + * the offset parameters will be ignored by the caller. + * + * TODO: Right now the imagefilters sometimes return empty result bitmaps/ + * specialimages. That doesn't seem quite right. */ - bool filterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const; + sk_sp filterImage(SkSpecialImage* src, const Context&, SkIPoint* offset) const; + enum MapDirection { + kForward_MapDirection, + kReverse_MapDirection + }; /** - * Given the src bounds of an image, this returns the bounds of the result - * image after the filter has been applied. + * Map a device-space rect recursively forward or backward through the + * filter DAG. kForward_MapDirection is used to determine which pixels of + * the destination canvas a source image rect would touch after filtering. + * kReverse_MapDirection is used to determine which rect of the source + * image would be required to fill the given rect (typically, clip bounds). + * Used for clipping and temp-buffer allocations, so the result need not + * be exact, but should never be smaller than the real answer. The default + * implementation recursively unions all input bounds, or returns the + * source rect if no inputs. */ - bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const; + SkIRect filterBounds(const SkIRect& src, const SkMatrix& ctm, + MapDirection = kReverse_MapDirection) const; /** * Returns true if the filter can be processed on the GPU. This is most @@ -174,8 +193,8 @@ public: * relative to the src when it is drawn. The default implementation does * single-pass processing using asFragmentProcessor(). */ - virtual bool filterImageGPU(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const; + virtual bool filterImageGPUDeprecated(Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const; /** * Returns whether this image filter is a color filter and puts the color filter into the @@ -231,23 +250,42 @@ public: CropRect getCropRect() const { return fCropRect; } // Default impl returns union of all input bounds. - virtual void computeFastBounds(const SkRect&, SkRect*) const; + virtual SkRect computeFastBounds(const SkRect&) const; // Can this filter DAG compute the resulting bounds of an object-space rectangle? - virtual bool canComputeFastBounds() const; + bool canComputeFastBounds() const; /** * If this filter can be represented by another filter + a localMatrix, return that filter, * else return null. */ - SkImageFilter* newWithLocalMatrix(const SkMatrix& matrix) const; + sk_sp makeWithLocalMatrix(const SkMatrix&) const; + +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + SkImageFilter* newWithLocalMatrix(const SkMatrix& matrix) const { + return this->makeWithLocalMatrix(matrix).release(); + } +#endif /** * Create an SkMatrixImageFilter, which transforms its input by the given matrix. */ + static sk_sp MakeMatrixFilter(const SkMatrix& matrix, + SkFilterQuality, + sk_sp input); +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR static SkImageFilter* CreateMatrixFilter(const SkMatrix& matrix, - SkFilterQuality, - SkImageFilter* input = NULL); + SkFilterQuality filterQuality, + SkImageFilter* input = nullptr) { + return MakeMatrixFilter(matrix, filterQuality, sk_ref_sp(input)).release(); + } +#endif + + + sk_sp filterInput(int index, + SkSpecialImage* src, + const Context&, + SkIPoint* offset) const; #if SK_SUPPORT_GPU // Helper function which invokes GPU filter processing on the @@ -256,8 +294,9 @@ public: // has a GPU implementation, it will be invoked directly. // Otherwise, the filter will be processed in software and // uploaded to the GPU. - bool filterInputGPU(int index, SkImageFilter::Proxy* proxy, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const; + bool filterInputGPUDeprecated(int index, SkImageFilter::Proxy* proxy, + const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const; #endif SK_TO_STRING_PUREVIRT() @@ -266,9 +305,6 @@ public: protected: class Common { public: - Common() {} - ~Common(); - /** * Attempt to unflatten the cropRect and the expected number of input filters. * If any number of input filters is valid, pass -1. @@ -281,9 +317,9 @@ protected: const CropRect& cropRect() const { return fCropRect; } int inputCount() const { return fInputs.count(); } - SkImageFilter** inputs() const { return fInputs.get(); } + sk_sp* inputs() const { return fInputs.get(); } - SkImageFilter* getInput(int index) const { return fInputs[index]; } + sk_sp getInput(int index) const { return fInputs[index]; } // If the caller wants a copy of the inputs, call this and it will transfer ownership // of the unflattened input filters to the caller. This is just a short-cut for copying @@ -294,12 +330,14 @@ protected: private: CropRect fCropRect; // most filters accept at most 2 input-filters - SkAutoSTArray<2, SkImageFilter*> fInputs; + SkAutoSTArray<2, sk_sp> fInputs; void allocInputs(int count); }; - SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect = NULL); + SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect = nullptr); + + SkImageFilter(sk_sp* inputs, int inputCount, const CropRect* cropRect); virtual ~SkImageFilter(); @@ -330,20 +368,24 @@ protected: * case both the result and offset parameters will be ignored by the * caller. */ - virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const; - // Given the bounds of the destination rect to be filled in device - // coordinates (first parameter), and the CTM, compute (conservatively) - // which rect of the source image would be required (third parameter). - // Used for clipping and temp-buffer allocations, so the result need not - // be exact, but should never be smaller than the real answer. The default - // implementation recursively unions all input bounds, or returns false if - // no inputs. - virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const; - enum MapDirection { - kForward_MapDirection, - kReverse_MapDirection - }; + virtual bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const; + + virtual sk_sp onFilterImage(SkSpecialImage* src, const Context&, + SkIPoint* offset) const; + + /** + * This function recurses into its inputs with the given rect (first + * argument), calls filterBounds() with the given map direction on each, + * and returns the union of those results. If a derived class has special + * recursion requirements (e.g., it has an input which does not participate + * in bounds computation), it can be overridden here. + * + * Note that this function is *not* responsible for mapping the rect for + * this node's filter bounds requirements (i.e., calling + * onFilterNodeBounds()); that is handled by filterBounds(). + */ + virtual SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const; /** * Performs a forwards or reverse mapping of the given rect to accommodate @@ -358,15 +400,15 @@ protected: * in both forward and reverse directions. Unlike * onFilterBounds(), this function is non-recursive. */ - virtual void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const; + virtual SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const; // Helper function which invokes filter processing on the input at the // specified "index". If the input is null, it leaves "result" and // "offset" untouched, and returns true. If the input is non-null, it // calls filterImage() on that input, and returns true on success. // i.e., return !getInput(index) || getInput(index)->filterImage(...); - bool filterInput(int index, Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const; + bool filterInputDeprecated(int index, Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const; /** * Return true (and return a ref'd colorfilter) if this node in the DAG is just a @@ -376,29 +418,31 @@ protected: return false; } - /** Given a "src" bitmap and its "srcOffset", computes source and - * destination bounds for this filter. Initial bounds are the - * "src" bitmap bounds offset by "srcOffset". "dstBounds" are - * computed by transforming the crop rect by the context's CTM, - * applying it to the initial bounds, and intersecting the result - * with the context's clip bounds. "srcBounds" (if non-null) are - * computed by intersecting the initial bounds with "dstBounds", to - * ensure that we never sample outside of the crop rect (this restriction - * may be relaxed in the future). + /** Given a "srcBounds" rect, computes destination bounds for this + * destination bounds for this filter. "dstBounds" are computed by + * transforming the crop rect by the context's CTM, applying it to the + * initial bounds, and intersecting the result with the context's clip + * bounds. "srcBounds" (if non-null) are computed by intersecting the + * initial bounds with "dstBounds", to ensure that we never sample + * outside of the crop rect (this restriction may be relaxed in the + * future). */ - bool applyCropRect(const Context&, const SkBitmap& src, const SkIPoint& srcOffset, - SkIRect* dstBounds, SkIRect* srcBounds = nullptr) const; + bool applyCropRect(const Context&, const SkIRect& srcBounds, SkIRect* dstBounds) const; - /** Same as the above call, except that if the resulting crop rect is not - * entirely contained by the source bitmap's bounds, it creates a new - * bitmap in "result" and pads the edges with transparent black. In that - * case, the srcOffset is modified to be the same as the bounds, since no - * further adjustment is needed by the caller. This version should only - * be used by filters which are not capable of processing a smaller - * source bitmap into a larger destination. + /** A variant of the above call which takes the original source bitmap and + * source offset. If the resulting crop rect is not entirely contained by + * the source bitmap's bounds, it creates a new bitmap in "result" and + * pads the edges with transparent black. In that case, the srcOffset is + * modified to be the same as the bounds, since no further adjustment is + * needed by the caller. This version should only be used by filters + * which are not capable of processing a smaller source bitmap into a + * larger destination. */ - bool applyCropRect(const Context&, Proxy* proxy, const SkBitmap& src, SkIPoint* srcOffset, - SkIRect* bounds, SkBitmap* result) const; + bool applyCropRectDeprecated(const Context&, Proxy* proxy, const SkBitmap& src, + SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* result) const; + + sk_sp applyCropRect(const Context&, SkSpecialImage* src, SkIPoint* srcOffset, + SkIRect* bounds) const; /** * Returns true if the filter can be expressed a single-pass @@ -430,7 +474,11 @@ private: friend class SkGraphics; static void PurgeCache(); + bool filterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const; + bool usesSrcInput() const { return fUsesSrcInput; } + virtual bool affectsTransparentBlack() const { return false; } typedef SkFlattenable INHERITED; int fInputCount; @@ -438,6 +486,8 @@ private: bool fUsesSrcInput; CropRect fCropRect; uint32_t fUniqueID; // Globally unique + mutable SkTArray fCacheKeys; + mutable SkMutex fMutex; }; /** diff --git a/gfx/skia/skia/include/core/SkImageGenerator.h b/gfx/skia/skia/include/core/SkImageGenerator.h index 86e3053a06..1a46f6b9cd 100644 --- a/gfx/skia/skia/include/core/SkImageGenerator.h +++ b/gfx/skia/skia/include/core/SkImageGenerator.h @@ -11,6 +11,7 @@ #include "SkBitmap.h" #include "SkColor.h" #include "SkImageInfo.h" +#include "SkYUVSizeInfo.h" class GrContext; class GrTexture; @@ -129,18 +130,26 @@ public: bool getPixels(const SkImageInfo& info, void* pixels, size_t rowBytes); /** - * If planes or rowBytes is NULL or if any entry in planes is NULL or if any entry in rowBytes - * is 0, this imagegenerator should output the sizes and return true if it can efficiently - * return YUV planar data. If it cannot, it should return false. Note that either planes and - * rowBytes are both fully defined and non NULL/non 0 or they are both NULL or have NULL or 0 - * entries only. Having only partial planes/rowBytes information is not supported. + * If decoding to YUV is supported, this returns true. Otherwise, this + * returns false and does not modify any of the parameters. * - * If all planes and rowBytes entries are non NULL or non 0, then it should copy the - * associated YUV data into those planes of memory supplied by the caller. It should validate - * that the sizes match what it expected. If the sizes do not match, it should return false. + * @param sizeInfo Output parameter indicating the sizes and required + * allocation widths of the Y, U, and V planes. + * @param colorSpace Output parameter. */ - bool getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace); + bool queryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const; + + /** + * Returns true on success and false on failure. + * This always attempts to perform a full decode. If the client only + * wants size, it should call queryYUV8(). + * + * @param sizeInfo Needs to exactly match the values returned by the + * query, except the WidthBytes may be larger than the + * recommendation (but not smaller). + * @param planes Memory for each of the Y, U, and V planes. + */ + bool getYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]); /** * If the generator can natively/efficiently return its pixels as a GPU image (backed by a @@ -248,9 +257,13 @@ protected: virtual bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount); - virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]); - virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace); + + virtual bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const { + return false; + } + virtual bool onGetYUV8Planes(const SkYUVSizeInfo&, void*[3] /*planes*/) { + return false; + } virtual GrTexture* onGenerateTexture(GrContext*, const SkIRect*) { return nullptr; diff --git a/gfx/skia/skia/include/core/SkImageInfo.h b/gfx/skia/skia/include/core/SkImageInfo.h index 4b82ae6e2f..c55edd36c2 100644 --- a/gfx/skia/skia/include/core/SkImageInfo.h +++ b/gfx/skia/skia/include/core/SkImageInfo.h @@ -73,8 +73,9 @@ enum SkColorType { kBGRA_8888_SkColorType, kIndex_8_SkColorType, kGray_8_SkColorType, + kRGBA_F16_SkColorType, - kLastEnum_SkColorType = kGray_8_SkColorType, + kLastEnum_SkColorType = kRGBA_F16_SkColorType, #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) kN32_SkColorType = kBGRA_8888_SkColorType, @@ -95,6 +96,7 @@ static int SkColorTypeBytesPerPixel(SkColorType ct) { 4, // BGRA_8888 1, // kIndex_8 1, // kGray_8 + 8, // kRGBA_F16 }; static_assert(SK_ARRAY_COUNT(gSize) == (size_t)(kLastEnum_SkColorType + 1), "size_mismatch_with_SkColorType_enum"); @@ -114,6 +116,7 @@ static inline bool SkColorTypeIsValid(unsigned value) { static inline size_t SkColorTypeComputeOffset(SkColorType ct, int x, int y, size_t rowBytes) { int shift = 0; switch (SkColorTypeBytesPerPixel(ct)) { + case 8: shift = 3; break; case 4: shift = 2; break; case 2: shift = 1; break; case 1: shift = 0; break; @@ -315,4 +318,14 @@ private: {} }; +/////////////////////////////////////////////////////////////////////////////// + +static inline bool SkColorAndProfileAreGammaCorrect(SkColorType ct, SkColorProfileType pt) { + return kSRGB_SkColorProfileType == pt || kRGBA_F16_SkColorType == ct; +} + +static inline bool SkImageInfoIsGammaCorrect(const SkImageInfo& info) { + return SkColorAndProfileAreGammaCorrect(info.colorType(), info.profileType()); +} + #endif diff --git a/gfx/skia/skia/include/core/SkMath.h b/gfx/skia/skia/include/core/SkMath.h index e5069592d0..abb685f6da 100644 --- a/gfx/skia/skia/include/core/SkMath.h +++ b/gfx/skia/skia/include/core/SkMath.h @@ -72,7 +72,7 @@ int32_t SkSqrtBits(int32_t value, int bitBias); int SkCLZ_portable(uint32_t); #ifndef SkCLZ - #if defined(_MSC_VER) && _MSC_VER >= 1400 + #if defined(_MSC_VER) #include static inline int SkCLZ(uint32_t mask) { diff --git a/gfx/skia/skia/include/core/SkMatrix.h b/gfx/skia/skia/include/core/SkMatrix.h index 0ebe3280e2..adebada445 100644 --- a/gfx/skia/skia/include/core/SkMatrix.h +++ b/gfx/skia/skia/include/core/SkMatrix.h @@ -591,11 +591,16 @@ public: return GetMapPtsProc(this->getType()); } - /** If the matrix can be stepped in X (not complex perspective) - then return true and if step[XY] is not null, return the step[XY] value. - If it cannot, return false and ignore step. + /** Returns true if the matrix can be stepped in X (not complex + perspective). */ - bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const; + bool isFixedStepInX() const; + + /** If the matrix can be stepped in X (not complex perspective) + then return the step value. + If it cannot, behavior is undefined. + */ + SkVector fixedStepInX(SkScalar y) const; /** Efficient comparison of two matrices. It distinguishes between zero and * negative zero. It will return false when the sign of zero values is the diff --git a/gfx/skia/skia/src/ports/SkTime_Unix.cpp b/gfx/skia/skia/include/core/SkMilestone.h similarity index 58% rename from gfx/skia/skia/src/ports/SkTime_Unix.cpp rename to gfx/skia/skia/include/core/SkMilestone.h index 2123a40619..1b93a7b84d 100644 --- a/gfx/skia/skia/src/ports/SkTime_Unix.cpp +++ b/gfx/skia/skia/include/core/SkMilestone.h @@ -1,7 +1,9 @@ - /* - * Copyright 2006 The Android Open Source Project + * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +#ifndef SK_MILESTONE +#define SK_MILESTONE 51 +#endif diff --git a/gfx/skia/skia/include/core/SkMultiPictureDraw.h b/gfx/skia/skia/include/core/SkMultiPictureDraw.h index 8d24e615e8..cd46a303ae 100644 --- a/gfx/skia/skia/include/core/SkMultiPictureDraw.h +++ b/gfx/skia/skia/include/core/SkMultiPictureDraw.h @@ -8,8 +8,8 @@ #ifndef SkMultiPictureDraw_DEFINED #define SkMultiPictureDraw_DEFINED +#include "../private/SkTDArray.h" #include "SkMatrix.h" -#include "SkTDArray.h" class SkCanvas; class SkPaint; diff --git a/gfx/skia/skia/include/core/SkPaint.h b/gfx/skia/skia/include/core/SkPaint.h index 51e8848a00..57122550b3 100644 --- a/gfx/skia/skia/include/core/SkPaint.h +++ b/gfx/skia/skia/include/core/SkPaint.h @@ -13,7 +13,6 @@ #include "SkMatrix.h" #include "SkXfermode.h" -class SkAnnotation; class SkAutoDescriptor; class SkAutoGlyphCache; class SkColorFilter; @@ -35,11 +34,6 @@ class SkShader; class SkSurfaceProps; class SkTypeface; -typedef const SkGlyph& (*SkDrawCacheProc)(SkGlyphCache*, const char**, - SkFixed x, SkFixed y); - -typedef const SkGlyph& (*SkMeasureCacheProc)(SkGlyphCache*, const char**); - #define kBicubicFilterBitmap_Flag kHighQualityFilterBitmap_Flag /** \class SkPaint @@ -47,14 +41,15 @@ typedef const SkGlyph& (*SkMeasureCacheProc)(SkGlyphCache*, const char**); The SkPaint class holds the style and color information about how to draw geometries, text and bitmaps. */ - class SK_API SkPaint { public: SkPaint(); SkPaint(const SkPaint& paint); + SkPaint(SkPaint&& paint); ~SkPaint(); SkPaint& operator=(const SkPaint&); + SkPaint& operator=(SkPaint&&); /** operator== may give false negatives: two paints that draw equivalently may return false. It will never give false positives: two paints that @@ -480,7 +475,7 @@ public: The shader's reference count is not affected. @return the paint's shader (or NULL) */ - SkShader* getShader() const { return fShader; } + SkShader* getShader() const { return fShader.get(); } /** Set or clear the shader object. * Shaders specify the source color(s) for what is being drawn. If a paint @@ -503,13 +498,16 @@ public: * @param shader May be NULL. The shader to be installed in the paint * @return shader */ + void setShader(sk_sp); +#ifdef SK_SUPPORT_LEGACY_CREATESHADER_PTR SkShader* setShader(SkShader* shader); +#endif /** Get the paint's colorfilter. If there is a colorfilter, its reference count is not changed. @return the paint's colorfilter (or NULL) */ - SkColorFilter* getColorFilter() const { return fColorFilter; } + SkColorFilter* getColorFilter() const { return fColorFilter.get(); } /** Set or clear the paint's colorfilter, returning the parameter.

@@ -518,14 +516,17 @@ public: @param filter May be NULL. The filter to be installed in the paint @return filter */ +#ifdef SK_SUPPORT_LEGACY_COLORFILTER_PTR SkColorFilter* setColorFilter(SkColorFilter* filter); +#endif + void setColorFilter(sk_sp); /** Get the paint's xfermode object.

The xfermode's reference count is not affected. @return the paint's xfermode (or NULL) */ - SkXfermode* getXfermode() const { return fXfermode; } + SkXfermode* getXfermode() const { return fXfermode.get(); } /** Set or clear the xfermode object.

@@ -537,7 +538,10 @@ public: paint @return xfermode */ + void setXfermode(sk_sp); +#ifdef SK_SUPPORT_LEGACY_XFERMODE_PTR SkXfermode* setXfermode(SkXfermode* xfermode); +#endif /** Create an xfermode based on the specified Mode, and assign it into the paint, returning the mode that was set. If the Mode is SrcOver, then @@ -550,7 +554,7 @@ public: The patheffect reference count is not affected. @return the paint's patheffect (or NULL) */ - SkPathEffect* getPathEffect() const { return fPathEffect; } + SkPathEffect* getPathEffect() const { return fPathEffect.get(); } /** Set or clear the patheffect object.

@@ -562,14 +566,17 @@ public: paint @return effect */ + void setPathEffect(sk_sp); +#ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR SkPathEffect* setPathEffect(SkPathEffect* effect); +#endif /** Get the paint's maskfilter object.

The maskfilter reference count is not affected. @return the paint's maskfilter (or NULL) */ - SkMaskFilter* getMaskFilter() const { return fMaskFilter; } + SkMaskFilter* getMaskFilter() const { return fMaskFilter.get(); } /** Set or clear the maskfilter object.

@@ -581,7 +588,10 @@ public: the paint @return maskfilter */ +#ifdef SK_SUPPORT_LEGACY_MASKFILTER_PTR SkMaskFilter* setMaskFilter(SkMaskFilter* maskfilter); +#endif + void setMaskFilter(sk_sp); // These attributes are for text/fonts @@ -591,7 +601,7 @@ public: measuring text. The typeface reference count is not affected. @return the paint's typeface (or NULL) */ - SkTypeface* getTypeface() const { return fTypeface; } + SkTypeface* getTypeface() const { return fTypeface.get(); } /** Set or clear the typeface object.

@@ -604,13 +614,14 @@ public: @return typeface */ SkTypeface* setTypeface(SkTypeface* typeface); + void setTypeface(sk_sp); /** Get the paint's rasterizer (or NULL).

The raster controls how paths/text are turned into alpha masks. @return the paint's rasterizer (or NULL) */ - SkRasterizer* getRasterizer() const { return fRasterizer; } + SkRasterizer* getRasterizer() const { return fRasterizer.get(); } /** Set or clear the rasterizer object.

@@ -623,19 +634,20 @@ public: the paint. @return rasterizer */ +#ifdef SK_SUPPORT_LEGACY_MINOR_EFFECT_PTR SkRasterizer* setRasterizer(SkRasterizer* rasterizer); +#endif + void setRasterizer(sk_sp); - SkImageFilter* getImageFilter() const { return fImageFilter; } + SkImageFilter* getImageFilter() const { return fImageFilter.get(); } SkImageFilter* setImageFilter(SkImageFilter*); - - SkAnnotation* getAnnotation() const { return fAnnotation; } - SkAnnotation* setAnnotation(SkAnnotation*); + void setImageFilter(sk_sp); /** * Return the paint's SkDrawLooper (if any). Does not affect the looper's * reference count. */ - SkDrawLooper* getLooper() const { return fLooper; } + SkDrawLooper* getLooper() const { return fLooper.get(); } /** * Set or clear the looper object. @@ -648,7 +660,10 @@ public: * @param looper May be NULL. The new looper to be installed in the paint. * @return looper */ +#ifdef SK_SUPPORT_LEGACY_MINOR_EFFECT_PTR SkDrawLooper* setLooper(SkDrawLooper* looper); +#endif + void setLooper(sk_sp); enum Align { kLeft_Align, @@ -883,15 +898,67 @@ public: SkRect bounds[] = NULL) const; /** Return the path (outline) for the specified text. - Note: just like SkCanvas::drawText, this will respect the Align setting - in the paint. - */ + * Note: just like SkCanvas::drawText, this will respect the Align setting + * in the paint. + * + * @param text the text + * @param length number of bytes of text + * @param x The x-coordinate of the origin of the text. + * @param y The y-coordinate of the origin of the text. + * @param path The outline of the text. + */ void getTextPath(const void* text, size_t length, SkScalar x, SkScalar y, SkPath* path) const; + /** Return the path (outline) for the specified text. + * Note: just like SkCanvas::drawText, this will respect the Align setting + * in the paint. + * + * @param text the text + * @param length number of bytes of text + * @param pos array of positions, used to position each character + * @param path The outline of the text. + */ void getPosTextPath(const void* text, size_t length, const SkPoint pos[], SkPath* path) const; + /** Return the number of intervals that intersect the intercept along the axis of the advance. + * The return count is zero or a multiple of two, and is at most the number of glyphs * 2 in + * the string. The caller may pass nullptr for intervals to determine the size of the interval + * array, or may conservatively pre-allocate an array with length * 2 entries. The computed + * intervals are cached by glyph to improve performance for multiple calls. + * This permits constructing an underline that skips the descenders. + * + * @param text the text + * @param length number of bytes of text + * @param x The x-coordinate of the origin of the text. + * @param y The y-coordinate of the origin of the text. + * @param bounds The lower and upper line parallel to the advance. + * @param array If not null, the found intersections. + * + * @return The number of intersections, which may be zero. + */ + int getTextIntercepts(const void* text, size_t length, SkScalar x, SkScalar y, + const SkScalar bounds[2], SkScalar* intervals) const; + + /** Return the number of intervals that intersect the intercept along the axis of the advance. + * The return count is zero or a multiple of two, and is at most the number of glyphs * 2 in + * string. The caller may pass nullptr for intervals to determine the size of the interval + * array, or may conservatively pre-allocate an array with length * 2 entries. The computed + * intervals are cached by glyph to improve performance for multiple calls. + * This permits constructing an underline that skips the descenders. + * + * @param text the text + * @param length number of bytes of text + * @param pos array of positions, used to position each character + * @param bounds The lower and upper line parallel to the advance. + * @param array If not null, the glyph bounds contained by the advance parallel lines. + * + * @return The number of intersections, which may be zero. + */ + int getPosTextIntercepts(const void* text, size_t length, const SkPoint pos[], + const SkScalar bounds[2], SkScalar* intervals) const; + /** * Return a rectangle that represents the union of the bounds of all * of the glyphs, but each one positioned at (0,0). This may be conservatively large, and @@ -976,19 +1043,20 @@ public: return SetTextMatrix(matrix, fTextSize, fTextScaleX, fTextSkewX); } + typedef const SkGlyph& (*GlyphCacheProc)(SkGlyphCache*, const char**); + SK_TO_STRING_NONVIRT() private: - SkTypeface* fTypeface; - SkPathEffect* fPathEffect; - SkShader* fShader; - SkXfermode* fXfermode; - SkMaskFilter* fMaskFilter; - SkColorFilter* fColorFilter; - SkRasterizer* fRasterizer; - SkDrawLooper* fLooper; - SkImageFilter* fImageFilter; - SkAnnotation* fAnnotation; + sk_sp fTypeface; + sk_sp fPathEffect; + sk_sp fShader; + sk_sp fXfermode; + sk_sp fMaskFilter; + sk_sp fColorFilter; + sk_sp fRasterizer; + sk_sp fLooper; + sk_sp fImageFilter; SkScalar fTextSize; SkScalar fTextScaleX; @@ -1012,25 +1080,29 @@ private: uint32_t fBitfieldsUInt; }; - SkDrawCacheProc getDrawCacheProc() const; - SkMeasureCacheProc getMeasureCacheProc(bool needFullMetrics) const; + GlyphCacheProc getGlyphCacheProc(bool needFullMetrics) const; SkScalar measure_text(SkGlyphCache*, const char* text, size_t length, int* count, SkRect* bounds) const; + enum class FakeGamma { + Off = 0, On + }; + /* * Allocs an SkDescriptor on the heap and return it to the caller as a refcnted * SkData. Caller is responsible for managing the lifetime of this object. */ void getScalerContextDescriptor(SkAutoDescriptor*, const SkSurfaceProps& surfaceProps, - const SkMatrix*, bool ignoreGamma) const; + FakeGamma fakeGamma, const SkMatrix*) const; - SkGlyphCache* detachCache(const SkSurfaceProps* surfaceProps, const SkMatrix*, - bool ignoreGamma) const; + SkGlyphCache* detachCache(const SkSurfaceProps* surfaceProps, FakeGamma fakeGamma, + const SkMatrix*) const; - void descriptorProc(const SkSurfaceProps* surfaceProps, const SkMatrix* deviceMatrix, + void descriptorProc(const SkSurfaceProps* surfaceProps, FakeGamma fakeGamma, + const SkMatrix* deviceMatrix, void (*proc)(SkTypeface*, const SkDescriptor*, void*), - void* context, bool ignoreGamma) const; + void* context) const; /* * The luminance color is used to determine which Gamma Canonical color to map to. This is @@ -1089,7 +1161,7 @@ private: friend class GrTextUtils; friend class GrGLPathRendering; friend class SkScalerContext; - friend class SkTextToPathIter; + friend class SkTextBaseIter; friend class SkCanonicalizePaint; }; diff --git a/gfx/skia/skia/include/core/SkPath.h b/gfx/skia/skia/include/core/SkPath.h index 4ce816f92f..8df7633d42 100644 --- a/gfx/skia/skia/include/core/SkPath.h +++ b/gfx/skia/skia/include/core/SkPath.h @@ -10,7 +10,6 @@ #include "SkMatrix.h" #include "SkPathRef.h" -#include "SkTDArray.h" #include "SkRefCnt.h" class SkReader32; @@ -37,6 +36,27 @@ public: return !(a == b); } + /** Return true if the paths contain an equal array of verbs and weights. Paths + * with equal verb counts can be readily interpolated. If the paths contain one + * or more conics, the conics' weights must also match. + * + * @param compare The path to compare. + * + * @return true if the paths have the same verbs and weights. + */ + bool isInterpolatable(const SkPath& compare) const; + + /** Interpolate between two paths with same-sized point arrays. + * The out path contains the verbs and weights of this path. + * The out points are a weighted average of this path and the ending path. + * + * @param ending The path to interpolate between. + * @param weight The weight, from 0 to 1. The output points are set to + * (this->points * weight) + ending->points * (1 - weight). + * @return true if the paths could be interpolated. + */ + bool interpolate(const SkPath& ending, SkScalar weight, SkPath* out) const; + #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK /** Returns true if the caller is the only owner of the underlying path data */ bool unique() const { return fPathRef->unique(); } @@ -187,6 +207,10 @@ public: return 0 == fPathRef->countVerbs(); } + /** Return true if the last contour of this path ends with a close verb. + */ + bool isLastContourClosed() const; + /** * Returns true if all of the points in this path are finite, meaning there * are no infinities and no NaNs. @@ -495,10 +519,12 @@ public: this->arcTo(p1.fX, p1.fY, p2.fX, p2.fY, radius); } - /** Close the current contour. If the current point is not equal to the - first point of the contour, a line segment is automatically added. - */ - void close(); + enum ArcSize { + /** the smaller of the two possible SVG arcs. */ + kSmall_ArcSize, + /** the larger of the two possible SVG arcs. */ + kLarge_ArcSize, + }; enum Direction { /** clockwise direction for adding closed contours */ @@ -507,6 +533,48 @@ public: kCCW_Direction, }; + /** + * Append an elliptical arc from the current point in the format used by SVG. + * The center of the ellipse is computed to satisfy the constraints below. + * + * @param rx,ry The radii in the x and y directions respectively. + * @param xAxisRotate The angle in degrees relative to the x-axis. + * @param largeArc Determines whether the smallest or largest arc possible + * is drawn. + * @param sweep Determines if the arc should be swept in an anti-clockwise or + * clockwise direction. Note that this enum value is opposite the SVG + * arc sweep value. + * @param x,y The destination coordinates. + */ + void arcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, + Direction sweep, SkScalar x, SkScalar y); + + void arcTo(const SkPoint r, SkScalar xAxisRotate, ArcSize largeArc, Direction sweep, + const SkPoint xy) { + this->arcTo(r.fX, r.fY, xAxisRotate, largeArc, sweep, xy.fX, xy.fY); + } + + /** Same as arcTo format used by SVG, but the destination coordinate is relative to the + * last point on this contour. If there is no previous point, then a + * moveTo(0,0) is inserted automatically. + * + * @param rx,ry The radii in the x and y directions respectively. + * @param xAxisRotate The angle in degrees relative to the x-axis. + * @param largeArc Determines whether the smallest or largest arc possible + * is drawn. + * @param sweep Determines if the arc should be swept in an anti-clockwise or + * clockwise direction. Note that this enum value is opposite the SVG + * arc sweep value. + * @param dx,dy The destination coordinates relative to the last point. + */ + void rArcTo(SkScalar rx, SkScalar ry, SkScalar xAxisRotate, ArcSize largeArc, + Direction sweep, SkScalar dx, SkScalar dy); + + /** Close the current contour. If the current point is not equal to the + first point of the contour, a line segment is automatically added. + */ + void close(); + /** * Returns whether or not a fill type is inverted * @@ -854,7 +922,7 @@ public: kQuad_Verb, //!< iter.next returns 3 points kConic_Verb, //!< iter.next returns 3 points + iter.conicWeight() kCubic_Verb, //!< iter.next returns 4 points - kClose_Verb, //!< iter.next returns 1 point (contour's moveTo pt) + kClose_Verb, //!< iter.next returns 0 points kDone_Verb, //!< iter.next returns 0 points }; @@ -1027,8 +1095,8 @@ private: enum SerializationVersions { kPathPrivFirstDirection_Version = 1, - - kCurrent_Version = 1 + kPathPrivLastMoveToIndex_Version = 2, + kCurrent_Version = 2 }; SkAutoTUnref fPathRef; @@ -1075,6 +1143,10 @@ private: bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts, bool* isClosed, Direction* direction) const; + // called by stroker to see if all points are equal and worthy of a cap + // equivalent to a short-circuit version of getBounds().isEmpty() + bool isZeroLength() const; + /** Returns if the path can return a bound at no cost (true) or will have to perform some computation (false). */ diff --git a/gfx/skia/skia/include/core/SkPathEffect.h b/gfx/skia/skia/include/core/SkPathEffect.h index 36e67e1de0..fd5957378a 100644 --- a/gfx/skia/skia/include/core/SkPathEffect.h +++ b/gfx/skia/skia/include/core/SkPathEffect.h @@ -14,7 +14,6 @@ #include "SkPath.h" #include "SkPoint.h" #include "SkRect.h" -#include "SkTDArray.h" class SkPath; class SkStrokeRec; @@ -155,16 +154,14 @@ private: for managing the lifetimes of its two arguments. */ class SkPairPathEffect : public SkPathEffect { -public: - virtual ~SkPairPathEffect(); - protected: - SkPairPathEffect(SkPathEffect* pe0, SkPathEffect* pe1); + SkPairPathEffect(sk_sp pe0, sk_sp pe1); void flatten(SkWriteBuffer&) const override; // these are visible to our subclasses - SkPathEffect* fPE0, *fPE1; + sk_sp fPE0; + sk_sp fPE1; SK_TO_STRING_OVERRIDE() @@ -184,10 +181,22 @@ public: The reference counts for outer and inner are both incremented in the constructor, and decremented in the destructor. */ - static SkComposePathEffect* Create(SkPathEffect* outer, SkPathEffect* inner) { - return new SkComposePathEffect(outer, inner); + static sk_sp Make(sk_sp outer, sk_sp inner) { + if (!outer) { + return inner; + } + if (!inner) { + return outer; + } + return sk_sp(new SkComposePathEffect(outer, inner)); } +#ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR + static SkPathEffect* Create(SkPathEffect* outer, SkPathEffect* inner) { + return Make(sk_ref_sp(outer), sk_ref_sp(inner)).release(); + } +#endif + virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override; @@ -199,7 +208,8 @@ public: #endif protected: - SkComposePathEffect(SkPathEffect* outer, SkPathEffect* inner) : INHERITED(outer, inner) {} + SkComposePathEffect(sk_sp outer, sk_sp inner) + : INHERITED(outer, inner) {} private: // illegal @@ -221,10 +231,21 @@ public: The reference counts for first and second are both incremented in the constructor, and decremented in the destructor. */ - static SkSumPathEffect* Create(SkPathEffect* first, SkPathEffect* second) { - return new SkSumPathEffect(first, second); + static sk_sp Make(sk_sp first, sk_sp second) { + if (!first) { + return second; + } + if (!second) { + return first; + } + return sk_sp(new SkSumPathEffect(first, second)); } +#ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR + static SkPathEffect* Create(SkPathEffect* first, SkPathEffect* second) { + return Make(sk_ref_sp(first), sk_ref_sp(second)).release(); + } +#endif virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override; @@ -236,7 +257,8 @@ public: #endif protected: - SkSumPathEffect(SkPathEffect* first, SkPathEffect* second) : INHERITED(first, second) {} + SkSumPathEffect(sk_sp first, sk_sp second) + : INHERITED(first, second) {} private: // illegal diff --git a/gfx/skia/skia/include/core/SkPathMeasure.h b/gfx/skia/skia/include/core/SkPathMeasure.h index 7528736ba8..2a512b8c38 100644 --- a/gfx/skia/skia/include/core/SkPathMeasure.h +++ b/gfx/skia/skia/include/core/SkPathMeasure.h @@ -8,8 +8,8 @@ #ifndef SkPathMeasure_DEFINED #define SkPathMeasure_DEFINED +#include "../private/SkTDArray.h" #include "SkPath.h" -#include "SkTDArray.h" struct SkConic; @@ -20,8 +20,11 @@ public: for the lifetime of the measure object, or until setPath() is called with a different path (or null), since the measure object keeps a pointer to the path object (does not copy its data). + + resScale controls the precision of the measure. values > 1 increase the + precision (and possible slow down the computation). */ - SkPathMeasure(const SkPath& path, bool forceClosed); + SkPathMeasure(const SkPath& path, bool forceClosed, SkScalar resScale = 1); ~SkPathMeasure(); /** Reset the pathmeasure with the specified path. The path must remain valid @@ -60,7 +63,7 @@ public: /** Given a start and stop distance, return in dst the intervening segment(s). If the segment is zero-length, return false, else return true. - startD and stopD are pinned to legal values (0..getLength()). If startD <= stopD + startD and stopD are pinned to legal values (0..getLength()). If startD > stopD then return false (and leave dst untouched). Begin the segment with a moveTo if startWithMoveTo is true */ @@ -82,6 +85,7 @@ public: private: SkPath::Iter fIter; const SkPath* fPath; + SkScalar fTolerance; SkScalar fLength; // relative to the current contour int fFirstPtIndex; // relative to the current contour bool fIsClosed; // relative to the current contour @@ -90,11 +94,7 @@ private: struct Segment { SkScalar fDistance; // total distance up to this point unsigned fPtIndex; // index into the fPts array -#ifdef SK_SUPPORT_LEGACY_PATH_MEASURE_TVALUE - unsigned fTValue : 15; -#else unsigned fTValue : 30; -#endif unsigned fType : 2; SkScalar getScalarT() const; @@ -107,10 +107,16 @@ private: void buildSegments(); SkScalar compute_quad_segs(const SkPoint pts[3], SkScalar distance, int mint, int maxt, int ptIndex); - SkScalar compute_conic_segs(const SkConic&, SkScalar distance, int mint, int maxt, int ptIndex); + SkScalar compute_conic_segs(const SkConic&, SkScalar distance, + int mint, const SkPoint& minPt, + int maxt, const SkPoint& maxPt, int ptIndex); SkScalar compute_cubic_segs(const SkPoint pts[3], SkScalar distance, int mint, int maxt, int ptIndex); const Segment* distanceToSegment(SkScalar distance, SkScalar* t); + bool quad_too_curvy(const SkPoint pts[3]); + bool conic_too_curvy(const SkPoint& firstPt, const SkPoint& midTPt,const SkPoint& lastPt); + bool cheap_dist_exceeds_limit(const SkPoint& pt, SkScalar x, SkScalar y); + bool cubic_too_curvy(const SkPoint pts[4]); }; #endif diff --git a/gfx/skia/skia/include/core/SkPathRef.h b/gfx/skia/skia/include/core/SkPathRef.h index 6d0ec627e8..ce84a91d1a 100644 --- a/gfx/skia/skia/include/core/SkPathRef.h +++ b/gfx/skia/skia/include/core/SkPathRef.h @@ -9,12 +9,12 @@ #ifndef SkPathRef_DEFINED #define SkPathRef_DEFINED +#include "../private/SkTDArray.h" #include "SkMatrix.h" #include "SkPoint.h" #include "SkRRect.h" #include "SkRect.h" #include "SkRefCnt.h" -#include "SkTDArray.h" #include // ptrdiff_t class SkRBuffer; @@ -271,6 +271,8 @@ public: */ uint32_t writeSize() const; + void interpolate(const SkPathRef& ending, SkScalar weight, SkPathRef* out) const; + /** * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the * same ID then they have the same verbs and points. However, two path refs may have the same diff --git a/gfx/skia/skia/include/core/SkPicture.h b/gfx/skia/skia/include/core/SkPicture.h index 720747e9a3..808944b5ef 100644 --- a/gfx/skia/skia/include/core/SkPicture.h +++ b/gfx/skia/skia/include/core/SkPicture.h @@ -8,20 +8,23 @@ #ifndef SkPicture_DEFINED #define SkPicture_DEFINED -#include "SkImageDecoder.h" #include "SkRefCnt.h" +#include "SkRect.h" #include "SkTypes.h" class GrContext; class SkBigPicture; class SkBitmap; class SkCanvas; +class SkPath; class SkPictureData; class SkPixelSerializer; +class SkReadBuffer; class SkRefCntSet; class SkStream; class SkTypefacePlayback; class SkWStream; +class SkWriteBuffer; struct SkPictInfo; /** \class SkPicture @@ -54,8 +57,19 @@ public: * @return A new SkPicture representing the serialized data, or NULL if the stream is * invalid. */ - static SkPicture* CreateFromStream(SkStream*, - InstallPixelRefProc proc = &SkImageDecoder::DecodeMemory); + static sk_sp MakeFromStream(SkStream*, InstallPixelRefProc proc); + + /** + * Recreate a picture that was serialized into a stream. + * + * Any serialized images in the stream will be passed to + * SkImageGenerator::NewFromEncoded. + * + * @param SkStream Serialized picture data. Ownership is unchanged by this call. + * @return A new SkPicture representing the serialized data, or NULL if the stream is + * invalid. + */ + static sk_sp MakeFromStream(SkStream*); /** * Recreate a picture that was serialized into a buffer. If the creation requires bitmap @@ -65,7 +79,7 @@ public: * @return A new SkPicture representing the serialized data, or NULL if the buffer is * invalid. */ - static SkPicture* CreateFromBuffer(SkReadBuffer&); + static sk_sp MakeFromBuffer(SkReadBuffer&); /** * Subclasses of this can be passed to playback(). During the playback @@ -156,6 +170,18 @@ public: static void SetPictureIOSecurityPrecautionsEnabled_Dangerous(bool set); static bool PictureIOSecurityPrecautionsEnabled(); +#ifdef SK_SUPPORT_LEGACY_PICTURE_PTR + static SkPicture* CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { + return MakeFromStream(stream, proc).release(); + } + static SkPicture* CreateFromStream(SkStream* stream) { + return MakeFromStream(stream).release(); + } + static SkPicture* CreateFromBuffer(SkReadBuffer& rbuf) { + return MakeFromBuffer(rbuf).release(); + } +#endif + private: // Subclass whitelist. SkPicture(); @@ -164,9 +190,7 @@ private: template friend class SkMiniPicture; void serialize(SkWStream*, SkPixelSerializer*, SkRefCntSet* typefaces) const; - static SkPicture* CreateFromStream(SkStream*, - InstallPixelRefProc proc, - SkTypefacePlayback*); + static sk_sp MakeFromStream(SkStream*, InstallPixelRefProc, SkTypefacePlayback*); friend class SkPictureData; virtual int numSlowPaths() const = 0; @@ -181,10 +205,11 @@ private: // V41: Added serialization of SkBitmapSource's filterQuality parameter // V42: Added a bool to SkPictureShader serialization to indicate did-we-serialize-a-picture? // V43: Added DRAW_IMAGE and DRAW_IMAGE_RECT opt codes to serialized data + // V44: Move annotations from paint to drawAnnotation // Only SKPs within the min/current picture version range (inclusive) can be read. static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39. - static const uint32_t CURRENT_PICTURE_VERSION = 43; + static const uint32_t CURRENT_PICTURE_VERSION = 44; static_assert(MIN_PICTURE_VERSION <= 41, "Remove kFontFileName and related code from SkFontDescriptor.cpp."); @@ -194,9 +219,9 @@ private: static_assert(MIN_PICTURE_VERSION <= 43, "Remove SkBitmapSourceDeserializer."); - + static bool IsValidPictInfo(const SkPictInfo& info); - static SkPicture* Forwardport(const SkPictInfo&, const SkPictureData*); + static sk_sp Forwardport(const SkPictInfo&, const SkPictureData*); SkPictInfo createHeader() const; SkPictureData* backport() const; diff --git a/gfx/skia/skia/include/core/SkPictureRecorder.h b/gfx/skia/skia/include/core/SkPictureRecorder.h index 6f95860b70..3f0cbcc801 100644 --- a/gfx/skia/skia/include/core/SkPictureRecorder.h +++ b/gfx/skia/skia/include/core/SkPictureRecorder.h @@ -72,7 +72,7 @@ public: * reflect their current state, but will not contain a live reference to the drawables * themselves. */ - SkPicture* SK_WARN_UNUSED_RESULT endRecordingAsPicture(); + sk_sp finishRecordingAsPicture(); /** * Signal that the caller is done recording, and update the cull rect to use for bounding @@ -83,7 +83,7 @@ public: * and subsequent culling operations. * @return the picture containing the recorded content. */ - SkPicture* SK_WARN_UNUSED_RESULT endRecordingAsPicture(const SkRect& cullRect); + sk_sp finishRecordingAsPictureWithCull(const SkRect& cullRect); /** * Signal that the caller is done recording. This invalidates the canvas returned by @@ -95,10 +95,20 @@ public: * and therefore this drawable will reflect the current state of those nested drawables anytime * it is drawn or a new picture is snapped from it (by calling drawable->newPictureSnapshot()). */ - SkDrawable* SK_WARN_UNUSED_RESULT endRecordingAsDrawable(); + sk_sp finishRecordingAsDrawable(); - // Legacy API -- use endRecordingAsPicture instead. +#ifdef SK_SUPPORT_LEGACY_PICTURE_PTR + SkPicture* SK_WARN_UNUSED_RESULT endRecordingAsPicture() { + return this->finishRecordingAsPicture().release(); + } + SkPicture* SK_WARN_UNUSED_RESULT endRecordingAsPicture(const SkRect& cullRect) { + return this->finishRecordingAsPictureWithCull(cullRect).release(); + } + SkDrawable* SK_WARN_UNUSED_RESULT endRecordingAsDrawable() { + return this->finishRecordingAsDrawable().release(); + } SkPicture* SK_WARN_UNUSED_RESULT endRecording() { return this->endRecordingAsPicture(); } +#endif private: void reset(); diff --git a/gfx/skia/skia/include/core/SkPixelRef.h b/gfx/skia/skia/include/core/SkPixelRef.h index 9cdcd85a79..651176ebbb 100644 --- a/gfx/skia/skia/include/core/SkPixelRef.h +++ b/gfx/skia/skia/include/core/SkPixelRef.h @@ -9,15 +9,16 @@ #define SkPixelRef_DEFINED #include "../private/SkAtomics.h" +#include "../private/SkMutex.h" +#include "../private/SkTDArray.h" #include "SkBitmap.h" #include "SkFilterQuality.h" #include "SkImageInfo.h" -#include "../private/SkMutex.h" #include "SkPixmap.h" #include "SkRefCnt.h" #include "SkSize.h" #include "SkString.h" -#include "SkTDArray.h" +#include "SkYUVSizeInfo.h" class SkColorTable; class SkData; @@ -210,19 +211,28 @@ public: virtual GrTexture* getTexture() { return NULL; } /** - * If any planes or rowBytes is NULL, this should output the sizes and return true - * if it can efficiently return YUV planar data. If it cannot, it should return false. + * If this can efficiently return YUV data, this should return true. + * Otherwise this returns false and does not modify any of the parameters. * - * If all planes and rowBytes are not NULL, then it should copy the associated Y,U,V data - * into those planes of memory supplied by the caller. It should validate that the sizes - * match what it expected. If the sizes do not match, it should return false. - * - * If colorSpace is not NULL, the YUV color space of the data should be stored in the address - * it points at. + * @param sizeInfo Output parameter indicating the sizes and required + * allocation widths of the Y, U, and V planes. + * @param colorSpace Output parameter. */ - bool getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) { - return this->onGetYUV8Planes(sizes, planes, rowBytes, colorSpace); + bool queryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const { + return this->onQueryYUV8(sizeInfo, colorSpace); + } + + /** + * Returns true on success and false on failure. + * Copies YUV data into the provided YUV planes. + * + * @param sizeInfo Needs to exactly match the values returned by the + * query, except the WidthBytes may be larger than the + * recommendation (but not smaller). + * @param planes Memory for each of the Y, U, and V planes. + */ + bool getYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) { + return this->onGetYUV8Planes(sizeInfo, planes); } /** Populates dst with the pixels of this pixelRef, converting them to colorType. */ @@ -308,9 +318,12 @@ protected: // default impl does nothing. virtual void onNotifyPixelsChanged(); - // default impl returns false. - virtual bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace); + virtual bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const { + return false; + } + virtual bool onGetYUV8Planes(const SkYUVSizeInfo&, void*[3] /*planes*/) { + return false; + } /** * Returns the size (in bytes) of the internally allocated memory. @@ -381,6 +394,7 @@ private: bool isPreLocked() const { return fPreLocked; } friend class SkImage_Raster; + friend class SkSpecialImage_Raster; // When copying a bitmap to another with the same shape and config, we can safely // clone the pixelref generation ID too, which makes them equivalent under caching. diff --git a/gfx/skia/skia/include/core/SkPixmap.h b/gfx/skia/skia/include/core/SkPixmap.h index da97025cab..5f90273a97 100644 --- a/gfx/skia/skia/include/core/SkPixmap.h +++ b/gfx/skia/skia/include/core/SkPixmap.h @@ -13,6 +13,7 @@ #include "SkImageInfo.h" class SkColorTable; +class SkData; struct SkMask; /** @@ -70,55 +71,90 @@ public: SkIRect bounds() const { return SkIRect::MakeWH(this->width(), this->height()); } + /** + * Return the rowbytes expressed as a number of pixels (like width and height). + */ + int rowBytesAsPixels() const { return int(fRowBytes >> this->shiftPerPixel()); } + + /** + * Return the shift amount per pixel (i.e. 0 for 1-byte per pixel, 1 for 2-bytes per pixel + * colortypes, 2 for 4-bytes per pixel colortypes). Return 0 for kUnknown_SkColorType. + */ + int shiftPerPixel() const { return fInfo.bytesPerPixel() >> 1; } + uint64_t getSize64() const { return sk_64_mul(fInfo.height(), fRowBytes); } uint64_t getSafeSize64() const { return fInfo.getSafeSize64(fRowBytes); } size_t getSafeSize() const { return fInfo.getSafeSize(fRowBytes); } - const uint32_t* addr32() const { - SkASSERT(4 == SkColorTypeBytesPerPixel(fInfo.colorType())); - return reinterpret_cast(fPixels); + const void* addr(int x, int y) const { + return (const char*)fPixels + fInfo.computeOffset(x, y, fRowBytes); } - - const uint16_t* addr16() const { - SkASSERT(2 == SkColorTypeBytesPerPixel(fInfo.colorType())); - return reinterpret_cast(fPixels); - } - const uint8_t* addr8() const { SkASSERT(1 == SkColorTypeBytesPerPixel(fInfo.colorType())); return reinterpret_cast(fPixels); } + const uint16_t* addr16() const { + SkASSERT(2 == SkColorTypeBytesPerPixel(fInfo.colorType())); + return reinterpret_cast(fPixels); + } + const uint32_t* addr32() const { + SkASSERT(4 == SkColorTypeBytesPerPixel(fInfo.colorType())); + return reinterpret_cast(fPixels); + } + const uint64_t* addr64() const { + SkASSERT(8 == SkColorTypeBytesPerPixel(fInfo.colorType())); + return reinterpret_cast(fPixels); + } + const uint16_t* addrF16() const { + SkASSERT(8 == SkColorTypeBytesPerPixel(fInfo.colorType())); + SkASSERT(kRGBA_F16_SkColorType == fInfo.colorType()); + return reinterpret_cast(fPixels); + } - const uint32_t* addr32(int x, int y) const { + // Offset by the specified x,y coordinates + + const uint8_t* addr8(int x, int y) const { SkASSERT((unsigned)x < (unsigned)fInfo.width()); SkASSERT((unsigned)y < (unsigned)fInfo.height()); - return (const uint32_t*)((const char*)this->addr32() + y * fRowBytes + (x << 2)); + return (const uint8_t*)((const char*)this->addr8() + y * fRowBytes + (x << 0)); } const uint16_t* addr16(int x, int y) const { SkASSERT((unsigned)x < (unsigned)fInfo.width()); SkASSERT((unsigned)y < (unsigned)fInfo.height()); return (const uint16_t*)((const char*)this->addr16() + y * fRowBytes + (x << 1)); } - const uint8_t* addr8(int x, int y) const { + const uint32_t* addr32(int x, int y) const { SkASSERT((unsigned)x < (unsigned)fInfo.width()); SkASSERT((unsigned)y < (unsigned)fInfo.height()); - return (const uint8_t*)((const char*)this->addr8() + y * fRowBytes + (x << 0)); + return (const uint32_t*)((const char*)this->addr32() + y * fRowBytes + (x << 2)); } - const void* addr(int x, int y) const { - return (const char*)fPixels + fInfo.computeOffset(x, y, fRowBytes); + const uint64_t* addr64(int x, int y) const { + SkASSERT((unsigned)x < (unsigned)fInfo.width()); + SkASSERT((unsigned)y < (unsigned)fInfo.height()); + return (const uint64_t*)((const char*)this->addr64() + y * fRowBytes + (x << 3)); + } + const uint16_t* addrF16(int x, int y) const { + SkASSERT(kRGBA_F16_SkColorType == fInfo.colorType()); + return reinterpret_cast(this->addr64(x, y)); } // Writable versions void* writable_addr() const { return const_cast(fPixels); } - uint32_t* writable_addr32(int x, int y) const { - return const_cast(this->addr32(x, y)); + uint8_t* writable_addr8(int x, int y) const { + return const_cast(this->addr8(x, y)); } uint16_t* writable_addr16(int x, int y) const { return const_cast(this->addr16(x, y)); } - uint8_t* writable_addr8(int x, int y) const { - return const_cast(this->addr8(x, y)); + uint32_t* writable_addr32(int x, int y) const { + return const_cast(this->addr32(x, y)); + } + uint64_t* writable_addr64(int x, int y) const { + return const_cast(this->addr64(x, y)); + } + uint16_t* writable_addrF16(int x, int y) const { + return reinterpret_cast(writable_addr64(x, y)); } // copy methods @@ -151,6 +187,7 @@ public: bool erase(SkColor, const SkIRect& subset) const; bool erase(SkColor color) const { return this->erase(color, this->bounds()); } + bool erase(const SkColor4f&, const SkIRect* subset = nullptr) const; private: const void* fPixels; @@ -161,59 +198,6 @@ private: ///////////////////////////////////////////////////////////////////////////////////////////// -class SK_API SkAutoPixmapStorage : public SkPixmap { -public: - SkAutoPixmapStorage(); - ~SkAutoPixmapStorage(); - - /** - * Try to allocate memory for the pixels needed to match the specified Info. On success - * return true and fill out the pixmap to point to that memory. The storage will be freed - * when this object is destroyed, or if another call to tryAlloc() or alloc() is made. - * - * On failure, return false and reset() the pixmap to empty. - */ - bool tryAlloc(const SkImageInfo&); - - /** - * Allocate memory for the pixels needed to match the specified Info and fill out the pixmap - * to point to that memory. The storage will be freed when this object is destroyed, - * or if another call to tryAlloc() or alloc() is made. - * - * If the memory cannot be allocated, calls sk_throw(). - */ - void alloc(const SkImageInfo&); - - // We wrap these so we can clear our internal storage - - void reset() { - this->freeStorage(); - this->INHERITED::reset(); - } - void reset(const SkImageInfo& info, const void* addr, size_t rb, SkColorTable* ctable = NULL) { - this->freeStorage(); - this->INHERITED::reset(info, addr, rb, ctable); - } - void reset(const SkImageInfo& info) { - this->freeStorage(); - this->INHERITED::reset(info); - } - bool SK_WARN_UNUSED_RESULT reset(const SkMask& mask) { - this->freeStorage(); - return this->INHERITED::reset(mask); - } - -private: - void* fStorage; - - void freeStorage() { - sk_free(fStorage); - fStorage = NULL; - } - - typedef SkPixmap INHERITED; -}; - ///////////////////////////////////////////////////////////////////////////////////////////// class SK_API SkAutoPixmapUnlock : ::SkNoncopyable { diff --git a/gfx/skia/skia/include/core/SkPngChunkReader.h b/gfx/skia/skia/include/core/SkPngChunkReader.h index f424dd8cfc..0cd6634bce 100644 --- a/gfx/skia/skia/include/core/SkPngChunkReader.h +++ b/gfx/skia/skia/include/core/SkPngChunkReader.h @@ -16,7 +16,7 @@ * * Base class for optional callbacks to retrieve meta/chunk data out of a PNG * encoded image as it is being decoded. - * Used by SkImageDecoder and SkCodec. + * Used by SkCodec. */ class SkPngChunkReader : public SkRefCnt { public: diff --git a/gfx/skia/skia/include/core/SkPostConfig.h b/gfx/skia/skia/include/core/SkPostConfig.h index 387ea5b154..a7fbba7c78 100644 --- a/gfx/skia/skia/include/core/SkPostConfig.h +++ b/gfx/skia/skia/include/core/SkPostConfig.h @@ -103,17 +103,6 @@ // TODO(mdempsky): Move elsewhere as appropriate. #include -#ifndef SK_CRASH -# ifdef SK_BUILD_FOR_WIN -# define SK_CRASH() __debugbreak() -# else -# if 1 // set to 0 for infinite loop, which can help connecting gdb -# define SK_CRASH() do { SkNO_RETURN_HINT(); *(int *)(uintptr_t)0xbbadbeef = 0; } while (false) -# else -# define SK_CRASH() do { SkNO_RETURN_HINT(); } while (true) -# endif -# endif -#endif /////////////////////////////////////////////////////////////////////////////// @@ -148,31 +137,21 @@ #endif #if defined(GOOGLE3) - // Used as argument to DumpStackTrace in SK_ALWAYSBREAK. void SkDebugfForDumpStackTrace(const char* data, void* unused); + void DumpStackTrace(int skip_count, void w(const char*, void*), void* arg); +# define SK_DUMP_GOOGLE3_STACK() DumpStackTrace(0, SkDebugfForDumpStackTrace, nullptr) +#else +# define SK_DUMP_GOOGLE3_STACK() #endif -#ifndef SK_ALWAYSBREAK -# if defined(GOOGLE3) - void DumpStackTrace(int skip_count, void w(const char*, void*), - void* arg); -# define SK_ALWAYSBREAK(cond) do { \ - if (cond) break; \ - SkNO_RETURN_HINT(); \ - SkDebugf("%s:%d: failed assertion \"%s\"\n", __FILE__, __LINE__, #cond); \ - DumpStackTrace(0, SkDebugfForDumpStackTrace, nullptr); \ - SK_CRASH(); \ - } while (false) -# elif defined(SK_DEBUG) -# define SK_ALWAYSBREAK(cond) do { \ - if (cond) break; \ - SkNO_RETURN_HINT(); \ - SkDebugf("%s:%d: failed assertion \"%s\"\n", __FILE__, __LINE__, #cond); \ - SK_CRASH(); \ - } while (false) -# else -# define SK_ALWAYSBREAK(cond) do { if (cond) break; SK_CRASH(); } while (false) -# endif +#ifndef SK_ABORT +# define SK_ABORT(msg) \ + do { \ + SkNO_RETURN_HINT(); \ + SkDebugf("%s:%d: fatal error: \"%s\"\n", __FILE__, __LINE__, #msg); \ + SK_DUMP_GOOGLE3_STACK(); \ + sk_abort_no_print(); \ + } while (false) #endif /** @@ -365,4 +344,14 @@ # define GR_TEST_UTILS 1 #endif +////////////////////////////////////////////////////////////////////// + +#ifndef SK_HISTOGRAM_BOOLEAN +# define SK_HISTOGRAM_BOOLEAN(name, value) +#endif + +#ifndef SK_HISTOGRAM_ENUMERATION +# define SK_HISTOGRAM_ENUMERATION(name, value, boundary_value) +#endif + #endif // SkPostConfig_DEFINED diff --git a/gfx/skia/skia/include/core/SkPreConfig.h b/gfx/skia/skia/include/core/SkPreConfig.h index c17cc18e11..b9b46c073a 100644 --- a/gfx/skia/skia/include/core/SkPreConfig.h +++ b/gfx/skia/skia/include/core/SkPreConfig.h @@ -118,6 +118,10 @@ #define SK_CPU_SSE_LEVEL_AVX 51 #define SK_CPU_SSE_LEVEL_AVX2 52 +#ifdef SK_BUILD_FOR_IOS + #define SK_CPU_SSE_LEVEL 0 // We're tired of fighting with opts/ and iOS simulator. +#endif + // Are we in GCC? #ifndef SK_CPU_SSE_LEVEL // These checks must be done in descending order to ensure we set the highest diff --git a/gfx/skia/skia/include/core/SkRRect.h b/gfx/skia/skia/include/core/SkRRect.h index 994edd2cc1..5564ce0d7e 100644 --- a/gfx/skia/skia/include/core/SkRRect.h +++ b/gfx/skia/skia/include/core/SkRRect.h @@ -326,6 +326,7 @@ private: void computeType(); bool checkCornerContainment(SkScalar x, SkScalar y) const; + void scaleRadii(); // to access fRadii directly friend class SkPath; diff --git a/gfx/skia/skia/src/core/SkRWBuffer.h b/gfx/skia/skia/include/core/SkRWBuffer.h similarity index 96% rename from gfx/skia/skia/src/core/SkRWBuffer.h rename to gfx/skia/skia/include/core/SkRWBuffer.h index 9d88a60203..106e572587 100644 --- a/gfx/skia/skia/src/core/SkRWBuffer.h +++ b/gfx/skia/skia/include/core/SkRWBuffer.h @@ -19,15 +19,15 @@ class SkStreamAsset; * Contains a read-only, thread-sharable block of memory. To access the memory, the caller must * instantiate a local iterator, as the memory is stored in 1 or more contiguous blocks. */ -class SkROBuffer : public SkRefCnt { +class SK_API SkROBuffer : public SkRefCnt { public: /** * Return the logical length of the data owned/shared by this buffer. It may be stored in * multiple contiguous blocks, accessible via the iterator. */ size_t size() const { return fUsed; } - - class Iter { + + class SK_API Iter { public: Iter(const SkROBuffer*); @@ -58,10 +58,10 @@ public: private: SkROBuffer(const SkBufferHead* head, size_t used); virtual ~SkROBuffer(); - + const SkBufferHead* fHead; const size_t fUsed; - + friend class SkRWBuffer; }; @@ -70,18 +70,18 @@ private: * The growth is done such that at any time, a RBuffer or StreamAsset can be snapped off, which * can see the previously stored bytes, but which will be unaware of any future writes. */ -class SkRWBuffer { +class SK_API SkRWBuffer { public: SkRWBuffer(size_t initialCapacity = 0); ~SkRWBuffer(); - + size_t size() const { return fTotalUsed; } void append(const void* buffer, size_t length); void* append(size_t length); SkROBuffer* newRBufferSnapshot() const; SkStreamAsset* newStreamSnapshot() const; - + #ifdef SK_DEBUG void validate() const; #else diff --git a/gfx/skia/skia/include/core/SkRect.h b/gfx/skia/skia/include/core/SkRect.h index 4f649b7c73..3ebe099ae6 100644 --- a/gfx/skia/skia/include/core/SkRect.h +++ b/gfx/skia/skia/include/core/SkRect.h @@ -463,8 +463,7 @@ struct SK_API SkRect { /** * Returns true iff all values in the rect are finite. If any are - * infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this - * returns false. + * infinite or NaN then this returns false. */ bool isFinite() const { float accum = 0; diff --git a/gfx/skia/skia/include/core/SkRefCnt.h b/gfx/skia/skia/include/core/SkRefCnt.h index 7590032968..e4fcbfcf74 100644 --- a/gfx/skia/skia/include/core/SkRefCnt.h +++ b/gfx/skia/skia/include/core/SkRefCnt.h @@ -9,8 +9,13 @@ #define SkRefCnt_DEFINED #include "../private/SkAtomics.h" -#include "../private/SkUniquePtr.h" +#include "../private/SkTLogic.h" #include "SkTypes.h" +#include +#include +#include + +#define SK_SUPPORT_TRANSITION_TO_SP_INTERFACES /** \class SkRefCntBase @@ -183,12 +188,16 @@ template struct SkTUnref { /** * Utility class that simply unref's its argument in the destructor. */ -template class SkAutoTUnref : public skstd::unique_ptr> { +template class SkAutoTUnref : public std::unique_ptr> { public: - explicit SkAutoTUnref(T* obj = nullptr) : skstd::unique_ptr>(obj) {} + explicit SkAutoTUnref(T* obj = nullptr) : std::unique_ptr>(obj) {} - T* detach() { return this->release(); } operator T*() const { return this->get(); } + +#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) + // Need to update graphics/Shader.cpp. + T* detach() { return this->release(); } +#endif }; // Can't use the #define trick below to guard a bare SkAutoTUnref(...) because it's templated. :( @@ -225,4 +234,221 @@ private: mutable int32_t fRefCnt; }; +/////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Shared pointer class to wrap classes that support a ref()/unref() interface. + * + * This can be used for classes inheriting from SkRefCnt, but it also works for other + * classes that match the interface, but have different internal choices: e.g. the hosted class + * may have its ref/unref be thread-safe, but that is not assumed/imposed by sk_sp. + */ +template class sk_sp { + /** Supports safe bool idiom. Obsolete with explicit operator bool. */ + using unspecified_bool_type = T* sk_sp::*; +public: + using element_type = T; + + sk_sp() : fPtr(nullptr) {} + sk_sp(std::nullptr_t) : fPtr(nullptr) {} + + /** + * Shares the underlying object by calling ref(), so that both the argument and the newly + * created sk_sp both have a reference to it. + */ + sk_sp(const sk_sp& that) : fPtr(SkSafeRef(that.get())) {} + template ::value>> + sk_sp(const sk_sp& that) : fPtr(SkSafeRef(that.get())) {} + + /** + * Move the underlying object from the argument to the newly created sk_sp. Afterwards only + * the new sk_sp will have a reference to the object, and the argument will point to null. + * No call to ref() or unref() will be made. + */ + sk_sp(sk_sp&& that) : fPtr(that.release()) {} + template ::value>> + sk_sp(sk_sp&& that) : fPtr(that.release()) {} + + /** + * Adopt the bare pointer into the newly created sk_sp. + * No call to ref() or unref() will be made. + */ + explicit sk_sp(T* obj) : fPtr(obj) {} + + /** + * Calls unref() on the underlying object pointer. + */ + ~sk_sp() { + SkSafeUnref(fPtr); + SkDEBUGCODE(fPtr = nullptr); + } + + sk_sp& operator=(std::nullptr_t) { this->reset(); return *this; } + + /** + * Shares the underlying object referenced by the argument by calling ref() on it. If this + * sk_sp previously had a reference to an object (i.e. not null) it will call unref() on that + * object. + */ + sk_sp& operator=(const sk_sp& that) { + this->reset(SkSafeRef(that.get())); + return *this; + } + template ::value>> + sk_sp& operator=(const sk_sp& that) { + this->reset(SkSafeRef(that.get())); + return *this; + } + + /** + * Move the underlying object from the argument to the sk_sp. If the sk_sp previously held + * a reference to another object, unref() will be called on that object. No call to ref() + * will be made. + */ + sk_sp& operator=(sk_sp&& that) { + this->reset(that.release()); + return *this; + } + template ::value>> + sk_sp& operator=(sk_sp&& that) { + this->reset(that.release()); + return *this; + } + + T& operator*() const { + SkASSERT(this->get() != nullptr); + return *this->get(); + } + + // MSVC 2013 does not work correctly with explicit operator bool. + // https://chromium-cpp.appspot.com/#core-blacklist + // When explicit operator bool can be used, remove operator! and operator unspecified_bool_type. + //explicit operator bool() const { return this->get() != nullptr; } + operator unspecified_bool_type() const { return this->get() ? &sk_sp::fPtr : nullptr; } + bool operator!() const { return this->get() == nullptr; } + + T* get() const { return fPtr; } + T* operator->() const { return fPtr; } + + /** + * Adopt the new bare pointer, and call unref() on any previously held object (if not null). + * No call to ref() will be made. + */ + void reset(T* ptr = nullptr) { + // Calling fPtr->unref() may call this->~() or this->reset(T*). + // http://wg21.cmeerw.net/lwg/issue998 + // http://wg21.cmeerw.net/lwg/issue2262 + T* oldPtr = fPtr; + fPtr = ptr; + SkSafeUnref(oldPtr); + } + + /** + * Return the bare pointer, and set the internal object pointer to nullptr. + * The caller must assume ownership of the object, and manage its reference count directly. + * No call to unref() will be made. + */ + T* SK_WARN_UNUSED_RESULT release() { + T* ptr = fPtr; + fPtr = nullptr; + return ptr; + } + + void swap(sk_sp& that) /*noexcept*/ { + using std::swap; + swap(fPtr, that.fPtr); + } + +private: + T* fPtr; +}; + +template inline void swap(sk_sp& a, sk_sp& b) /*noexcept*/ { + a.swap(b); +} + +template inline bool operator==(const sk_sp& a, const sk_sp& b) { + return a.get() == b.get(); +} +template inline bool operator==(const sk_sp& a, std::nullptr_t) /*noexcept*/ { + return !a; +} +template inline bool operator==(std::nullptr_t, const sk_sp& b) /*noexcept*/ { + return !b; +} + +template inline bool operator!=(const sk_sp& a, const sk_sp& b) { + return a.get() != b.get(); +} +template inline bool operator!=(const sk_sp& a, std::nullptr_t) /*noexcept*/ { + return static_cast(a); +} +template inline bool operator!=(std::nullptr_t, const sk_sp& b) /*noexcept*/ { + return static_cast(b); +} + +template inline bool operator<(const sk_sp& a, const sk_sp& b) { + // Provide defined total order on sk_sp. + // http://wg21.cmeerw.net/lwg/issue1297 + // http://wg21.cmeerw.net/lwg/issue1401 . + return std::less()((void*)a.get(), (void*)b.get()); +} +template inline bool operator<(const sk_sp& a, std::nullptr_t) { + return std::less()(a.get(), nullptr); +} +template inline bool operator<(std::nullptr_t, const sk_sp& b) { + return std::less()(nullptr, b.get()); +} + +template inline bool operator<=(const sk_sp& a, const sk_sp& b) { + return !(b < a); +} +template inline bool operator<=(const sk_sp& a, std::nullptr_t) { + return !(nullptr < a); +} +template inline bool operator<=(std::nullptr_t, const sk_sp& b) { + return !(b < nullptr); +} + +template inline bool operator>(const sk_sp& a, const sk_sp& b) { + return b < a; +} +template inline bool operator>(const sk_sp& a, std::nullptr_t) { + return nullptr < a; +} +template inline bool operator>(std::nullptr_t, const sk_sp& b) { + return b < nullptr; +} + +template inline bool operator>=(const sk_sp& a, const sk_sp& b) { + return !(a < b); +} +template inline bool operator>=(const sk_sp& a, std::nullptr_t) { + return !(a < nullptr); +} +template inline bool operator>=(std::nullptr_t, const sk_sp& b) { + return !(nullptr < b); +} + +template +sk_sp sk_make_sp(Args&&... args) { + return sk_sp(new T(std::forward(args)...)); +} + +#ifdef SK_SUPPORT_TRANSITION_TO_SP_INTERFACES + +/* + * Returns a sk_sp wrapping the provided ptr AND calls ref on it (if not null). + * + * This is different than the semantics of the constructor for sk_sp, which just wraps the ptr, + * effectively "adopting" it. + * + * This function may be helpful while we convert callers from ptr-based to sk_sp-based parameters. + */ +template sk_sp sk_ref_sp(T* obj) { + return sk_sp(SkSafeRef(obj)); +} + +#endif + #endif diff --git a/gfx/skia/skia/include/core/SkScalar.h b/gfx/skia/skia/include/core/SkScalar.h index 4efa8417af..b2d966cb3b 100644 --- a/gfx/skia/skia/include/core/SkScalar.h +++ b/gfx/skia/skia/include/core/SkScalar.h @@ -8,7 +8,6 @@ #ifndef SkScalar_DEFINED #define SkScalar_DEFINED -#include "SkFixed.h" #include "../private/SkFloatingPoint.h" // TODO: move this sort of check into SkPostConfig.h @@ -32,12 +31,10 @@ typedef float SkScalar; #define SK_ScalarNegativeInfinity SK_FloatNegativeInfinity #define SK_ScalarNaN SK_FloatNaN -#define SkFixedToScalar(x) SkFixedToFloat(x) -#define SkScalarToFixed(x) SkFloatToFixed(x) - #define SkScalarFloorToScalar(x) sk_float_floor(x) #define SkScalarCeilToScalar(x) sk_float_ceil(x) #define SkScalarRoundToScalar(x) sk_float_floor((x) + 0.5f) +#define SkScalarTruncToScalar(x) sk_float_trunc(x) #define SkScalarFloorToInt(x) sk_float_floor2int(x) #define SkScalarCeilToInt(x) sk_float_ceil2int(x) @@ -46,7 +43,6 @@ typedef float SkScalar; #define SkScalarAbs(x) sk_float_abs(x) #define SkScalarCopySign(x, y) sk_float_copysign(x, y) #define SkScalarMod(x, y) sk_float_mod(x,y) -#define SkScalarFraction(x) sk_float_mod(x, 1.0f) #define SkScalarSqrt(x) sk_float_sqrt(x) #define SkScalarPow(b, e) sk_float_pow(b, e) @@ -75,12 +71,10 @@ typedef double SkScalar; #define SK_ScalarNegativeInfinity SK_DoubleNegativeInfinity #define SK_ScalarNaN SK_DoubleNaN -#define SkFixedToScalar(x) SkFixedToDouble(x) -#define SkScalarToFixed(x) SkDoubleToFixed(x) - #define SkScalarFloorToScalar(x) floor(x) #define SkScalarCeilToScalar(x) ceil(x) #define SkScalarRoundToScalar(x) floor((x) + 0.5) +#define SkScalarTruncToScalar(x) trunc(x) #define SkScalarFloorToInt(x) (int)floor(x) #define SkScalarCeilToInt(x) (int)ceil(x) @@ -89,7 +83,6 @@ typedef double SkScalar; #define SkScalarAbs(x) abs(x) #define SkScalarCopySign(x, y) copysign(x, y) #define SkScalarMod(x, y) fmod(x,y) -#define SkScalarFraction(x) fmod(x, 1.0) #define SkScalarSqrt(x) sqrt(x) #define SkScalarPow(b, e) pow(b, e) @@ -168,6 +161,11 @@ static inline int SkDScalarRoundToInt(SkScalar x) { return (int)floor(xx); } +/** Returns the fractional part of the scalar. */ +static inline SkScalar SkScalarFraction(SkScalar x) { + return x - SkScalarTruncToScalar(x); +} + static inline SkScalar SkScalarClampMax(SkScalar x, SkScalar max) { x = SkTMin(x, max); x = SkTMax(x, 0); @@ -218,7 +216,7 @@ static inline SkScalar SkScalarSignAsScalar(SkScalar x) { #define SK_ScalarNearlyZero (SK_Scalar1 / (1 << 12)) static inline bool SkScalarNearlyZero(SkScalar x, - SkScalar tolerance = SK_ScalarNearlyZero) { + SkScalar tolerance = SK_ScalarNearlyZero) { SkASSERT(tolerance >= 0); return SkScalarAbs(x) <= tolerance; } diff --git a/gfx/skia/skia/include/core/SkShader.h b/gfx/skia/skia/include/core/SkShader.h index 60ef280d5e..0de53ad652 100644 --- a/gfx/skia/skia/include/core/SkShader.h +++ b/gfx/skia/skia/include/core/SkShader.h @@ -10,6 +10,7 @@ #include "SkBitmap.h" #include "SkFlattenable.h" +#include "SkImageInfo.h" #include "SkMask.h" #include "SkMatrix.h" #include "SkPaint.h" @@ -81,6 +82,10 @@ public: shadeSpan(). */ kConstInY32_Flag = 1 << 1, + + /** hint for the blitter that 4f is the preferred shading mode. + */ + kPrefers4f_Flag = 1 << 2, }; /** @@ -95,14 +100,22 @@ public: * ContextRec acts as a parameter bundle for creating Contexts. */ struct ContextRec { - ContextRec(const SkPaint& paint, const SkMatrix& matrix, const SkMatrix* localM) + enum DstType { + kPMColor_DstType, // clients prefer shading into PMColor dest + kPM4f_DstType, // clients prefer shading into PM4f dest + }; + + ContextRec(const SkPaint& paint, const SkMatrix& matrix, const SkMatrix* localM, + DstType dstType) : fPaint(&paint) , fMatrix(&matrix) - , fLocalMatrix(localM) {} + , fLocalMatrix(localM) + , fPreferredDstType(dstType) {} - const SkPaint* fPaint; // the current paint associated with the draw - const SkMatrix* fMatrix; // the current matrix in the canvas - const SkMatrix* fLocalMatrix; // optional local matrix + const SkPaint* fPaint; // the current paint associated with the draw + const SkMatrix* fMatrix; // the current matrix in the canvas + const SkMatrix* fLocalMatrix; // optional local matrix + const DstType fPreferredDstType; // the "natural" client dest type }; class Context : public ::SkNoncopyable { @@ -127,6 +140,37 @@ public: */ virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0; + virtual void shadeSpan4f(int x, int y, SkPM4f[], int count); + + struct BlitState; + typedef void (*BlitBW)(BlitState*, + int x, int y, const SkPixmap&, int count); + typedef void (*BlitAA)(BlitState*, + int x, int y, const SkPixmap&, int count, const SkAlpha[]); + + struct BlitState { + // inputs + Context* fCtx; + SkXfermode* fXfer; + + // outputs + enum { N = 2 }; + void* fStorage[N]; + BlitBW fBlitBW; + BlitAA fBlitAA; + }; + + // Returns true if one or more of the blitprocs are set in the BlitState + bool chooseBlitProcs(const SkImageInfo& info, BlitState* state) { + state->fBlitBW = nullptr; + state->fBlitAA = nullptr; + if (this->onChooseBlitProcs(info, state)) { + SkASSERT(state->fBlitBW || state->fBlitAA); + return true; + } + return false; + } + /** * The const void* ctx is only const because all the implementations are const. * This can be changed to non-const if a new shade proc needs to change the ctx. @@ -160,6 +204,9 @@ public: const SkMatrix& getTotalInverse() const { return fTotalInverse; } MatrixClass getInverseClass() const { return (MatrixClass)fTotalInverseClass; } const SkMatrix& getCTM() const { return fCTM; } + + virtual bool onChooseBlitProcs(const SkImageInfo&, BlitState*) { return false; } + private: SkMatrix fCTM; SkMatrix fTotalInverse; @@ -177,11 +224,8 @@ public: /** * Return the size of a Context returned by createContext. - * - * Override this if your subclass overrides createContext, to return the correct size of - * your subclass' context. */ - virtual size_t contextSize() const; + size_t contextSize(const ContextRec&) const; /** * Returns true if this shader is just a bitmap, and if not null, returns the bitmap, @@ -309,27 +353,62 @@ public: * Return a shader that will apply the specified localMatrix to this shader. * The specified matrix will be applied before any matrix associated with this shader. */ - SkShader* newWithLocalMatrix(const SkMatrix&) const; + sk_sp makeWithLocalMatrix(const SkMatrix&) const; /** * Create a new shader that produces the same colors as invoking this shader and then applying * the colorfilter. */ - SkShader* newWithColorFilter(SkColorFilter*) const; - + sk_sp makeWithColorFilter(sk_sp) const; + ////////////////////////////////////////////////////////////////////////// // Factory methods for stock shaders /** * Call this to create a new "empty" shader, that will not draw anything. */ - static SkShader* CreateEmptyShader(); + static sk_sp MakeEmptyShader(); /** * Call this to create a new shader that just draws the specified color. This should always * draw the same as a paint with this color (and no shader). */ - static SkShader* CreateColorShader(SkColor); + static sk_sp MakeColorShader(SkColor); + + static sk_sp MakeComposeShader(sk_sp dst, sk_sp src, + SkXfermode::Mode); + +#ifdef SK_SUPPORT_LEGACY_CREATESHADER_PTR + static SkShader* CreateEmptyShader() { return MakeEmptyShader().release(); } + static SkShader* CreateColorShader(SkColor c) { return MakeColorShader(c).release(); } + static SkShader* CreateBitmapShader(const SkBitmap& src, TileMode tmx, TileMode tmy, + const SkMatrix* localMatrix = nullptr) { + return MakeBitmapShader(src, tmx, tmy, localMatrix).release(); + } + static SkShader* CreateComposeShader(SkShader* dst, SkShader* src, SkXfermode::Mode mode); + static SkShader* CreateComposeShader(SkShader* dst, SkShader* src, SkXfermode* xfer); + static SkShader* CreatePictureShader(const SkPicture* src, TileMode tmx, TileMode tmy, + const SkMatrix* localMatrix, const SkRect* tile); + + SkShader* newWithLocalMatrix(const SkMatrix& matrix) const { + return this->makeWithLocalMatrix(matrix).release(); + } + SkShader* newWithColorFilter(SkColorFilter* filter) const; +#endif + + /** + * Create a new compose shader, given shaders dst, src, and a combining xfermode mode. + * The xfermode is called with the output of the two shaders, and its output is returned. + * If xfer is null, SkXfermode::kSrcOver_Mode is assumed. + * + * The caller is responsible for managing its reference-count for the xfer (if not null). + */ + static sk_sp MakeComposeShader(sk_sp dst, sk_sp src, + sk_sp xfer); +#ifdef SK_SUPPORT_LEGACY_XFERMODE_PTR + static sk_sp MakeComposeShader(sk_sp dst, sk_sp src, + SkXfermode* xfer); +#endif /** Call this to create a new shader that will draw with the specified bitmap. * @@ -345,9 +424,8 @@ public: * @param tmy The tiling mode to use when sampling the bitmap in the y-direction. * @return Returns a new shader object. Note: this function never returns null. */ - static SkShader* CreateBitmapShader(const SkBitmap& src, - TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix = NULL); + static sk_sp MakeBitmapShader(const SkBitmap& src, TileMode tmx, TileMode tmy, + const SkMatrix* localMatrix = nullptr); // NOTE: You can create an SkImage Shader with SkImage::newShader(). @@ -365,10 +443,8 @@ public: * bounds. * @return Returns a new shader object. Note: this function never returns null. */ - static SkShader* CreatePictureShader(const SkPicture* src, - TileMode tmx, TileMode tmy, - const SkMatrix* localMatrix, - const SkRect* tile); + static sk_sp MakePictureShader(sk_sp src, TileMode tmx, TileMode tmy, + const SkMatrix* localMatrix, const SkRect* tile); /** * If this shader can be represented by another shader + a localMatrix, return that shader @@ -393,6 +469,12 @@ protected: */ virtual Context* onCreateContext(const ContextRec&, void* storage) const; + /** + * Override this if your subclass overrides createContext, to return the correct size of + * your subclass' context. + */ + virtual size_t onContextSize(const ContextRec&) const; + virtual bool onAsLuminanceColor(SkColor*) const { return false; } diff --git a/gfx/skia/skia/include/core/SkStream.h b/gfx/skia/skia/include/core/SkStream.h index 4502416fd9..a6a62c42ba 100644 --- a/gfx/skia/skia/include/core/SkStream.h +++ b/gfx/skia/skia/include/core/SkStream.h @@ -8,11 +8,10 @@ #ifndef SkStream_DEFINED #define SkStream_DEFINED +#include "SkData.h" #include "SkRefCnt.h" #include "SkScalar.h" -class SkData; - class SkStream; class SkStreamRewindable; class SkStreamSeekable; @@ -270,11 +269,11 @@ public: const void* getMemoryBase() override; private: - FILE* fFILE; + FILE* fFILE; SkString fName; Ownership fOwnership; // fData is lazilly initialized when needed. - mutable SkAutoTUnref fData; + mutable sk_sp fData; typedef SkStreamAsset INHERITED; }; @@ -291,10 +290,12 @@ public: /** Use the specified data as the memory for this stream. * The stream will call ref() on the data (assuming it is not NULL). + * DEPRECATED */ SkMemoryStream(SkData*); - virtual ~SkMemoryStream(); + /** Creates the stream to read from the specified data */ + SkMemoryStream(sk_sp); /** Resets the stream to the specified data and length, just like the constructor. @@ -341,8 +342,8 @@ public: const void* getMemoryBase() override; private: - SkData* fData; - size_t fOffset; + sk_sp fData; + size_t fOffset; typedef SkStreamMemory INHERITED; }; @@ -369,7 +370,7 @@ private: typedef SkWStream INHERITED; }; -class SkMemoryWStream : public SkWStream { +class SK_API SkMemoryWStream : public SkWStream { public: SkMemoryWStream(void* buffer, size_t size); bool write(const void* buffer, size_t size) override; @@ -417,7 +418,7 @@ private: Block* fHead; Block* fTail; size_t fBytesWritten; - mutable SkData* fCopy; // is invalidated if we write after it is created + mutable sk_sp fCopy; // is invalidated if we write after it is created void invalidateCopy(); diff --git a/gfx/skia/skia/include/core/SkString.h b/gfx/skia/skia/include/core/SkString.h index 93514f2659..4a2d91f2df 100644 --- a/gfx/skia/skia/include/core/SkString.h +++ b/gfx/skia/skia/include/core/SkString.h @@ -10,8 +10,8 @@ #ifndef SkString_DEFINED #define SkString_DEFINED +#include "../private/SkTArray.h" #include "SkScalar.h" -#include "SkTArray.h" #include @@ -36,13 +36,13 @@ int SkStrStartsWithOneOf(const char string[], const char prefixes[]); static int SkStrFind(const char string[], const char substring[]) { const char *first = strstr(string, substring); if (NULL == first) return -1; - return SkToS32(first - &string[0]); + return SkToInt(first - &string[0]); } static int SkStrFindLastOf(const char string[], const char subchar) { const char* last = strrchr(string, subchar); if (NULL == last) return -1; - return SkToS32(last - &string[0]); + return SkToInt(last - &string[0]); } static bool SkStrContains(const char string[], const char substring[]) { @@ -111,7 +111,6 @@ char* SkStrAppendS64(char buffer[], int64_t, int minDigits); #define SkStrAppendScalar SkStrAppendFloat char* SkStrAppendFloat(char buffer[], float); -char* SkStrAppendFixed(char buffer[], SkFixed); /** \class SkString @@ -126,6 +125,7 @@ public: explicit SkString(const char text[]); SkString(const char text[], size_t len); SkString(const SkString&); + SkString(SkString&&); ~SkString(); bool isEmpty() const { return 0 == fRec->fLength; } @@ -172,6 +172,7 @@ public: // these methods edit the string SkString& operator=(const SkString&); + SkString& operator=(SkString&&); SkString& operator=(const char text[]); char* writable_str(); diff --git a/gfx/skia/skia/include/core/SkStrokeRec.h b/gfx/skia/skia/include/core/SkStrokeRec.h index 32e5440563..22981a5d9e 100644 --- a/gfx/skia/skia/include/core/SkStrokeRec.h +++ b/gfx/skia/skia/include/core/SkStrokeRec.h @@ -63,6 +63,10 @@ public: fMiterLimit = miterLimit; } + SkScalar getResScale() const { + return fResScale; + } + void setResScale(SkScalar rs) { SkASSERT(rs > 0 && SkScalarIsFinite(rs)); fResScale = rs; diff --git a/gfx/skia/skia/include/core/SkSurface.h b/gfx/skia/skia/include/core/SkSurface.h index 57527ed33a..ec76d26888 100644 --- a/gfx/skia/skia/include/core/SkSurface.h +++ b/gfx/skia/skia/include/core/SkSurface.h @@ -30,18 +30,6 @@ class GrRenderTarget; */ class SK_API SkSurface : public SkRefCnt { public: - /** - * Indicates whether a new surface or image should count against a cache budget. Currently this - * is only used by the GPU backend (sw-raster surfaces and images are never counted against the - * resource cache budget.) - */ - enum Budgeted { - /** The surface or image does not count against the cache budget. */ - kNo_Budgeted, - /** The surface or image counts against the cache budget. */ - kYes_Budgeted - }; - /** * Create a new surface, using the specified pixels/rowbytes as its * backend. @@ -49,43 +37,48 @@ public: * If the requested surface cannot be created, or the request is not a * supported configuration, NULL will be returned. */ - static SkSurface* NewRasterDirect(const SkImageInfo&, void* pixels, size_t rowBytes, - const SkSurfaceProps* = NULL); + static sk_sp MakeRasterDirect(const SkImageInfo&, void* pixels, size_t rowBytes, + const SkSurfaceProps* = nullptr); /** * The same as NewRasterDirect, but also accepts a call-back routine, which is invoked * when the surface is deleted, and is passed the pixel memory and the specified context. */ - static SkSurface* NewRasterDirectReleaseProc(const SkImageInfo&, void* pixels, size_t rowBytes, + static sk_sp MakeRasterDirectReleaseProc(const SkImageInfo&, void* pixels, size_t rowBytes, void (*releaseProc)(void* pixels, void* context), - void* context, const SkSurfaceProps* = NULL); + void* context, const SkSurfaceProps* = nullptr); /** - * Return a new surface, with the memory for the pixels automatically - * allocated. + * Return a new surface, with the memory for the pixels automatically allocated, but respecting + * the specified rowBytes. If rowBytes==0, then a default value will be chosen. If a non-zero + * rowBytes is specified, then any images snapped off of this surface (via newImageSnapshot()) + * are guaranteed to have the same rowBytes. * * If the requested surface cannot be created, or the request is not a * supported configuration, NULL will be returned. */ - static SkSurface* NewRaster(const SkImageInfo&, const SkSurfaceProps* = NULL); + static sk_sp MakeRaster(const SkImageInfo&, size_t rowBytes, const SkSurfaceProps*); + + /** + * Allocate a new surface, automatically computing the rowBytes. + */ + static sk_sp MakeRaster(const SkImageInfo&, const SkSurfaceProps* = nullptr); /** * Helper version of NewRaster. It creates a SkImageInfo with the * specified width and height, and populates the rest of info to match * pixels in SkPMColor format. */ - static SkSurface* NewRasterN32Premul(int width, int height, const SkSurfaceProps* props = NULL) { - return NewRaster(SkImageInfo::MakeN32Premul(width, height), props); + static sk_sp MakeRasterN32Premul(int width, int height, + const SkSurfaceProps* props = nullptr) { + return MakeRaster(SkImageInfo::MakeN32Premul(width, height), props); } /** * Return a new surface using the specified render target. */ - static SkSurface* NewRenderTargetDirect(GrRenderTarget*, const SkSurfaceProps*); - - static SkSurface* NewRenderTargetDirect(GrRenderTarget* target) { - return NewRenderTargetDirect(target, NULL); - } + static sk_sp MakeRenderTargetDirect(GrRenderTarget*, + const SkSurfaceProps* = nullptr); /** * Used to wrap a pre-existing backend 3D API texture as a SkSurface. The kRenderTarget flag @@ -93,34 +86,102 @@ public: * of the texture and the client must ensure the texture is valid for the lifetime of the * SkSurface. */ - static SkSurface* NewFromBackendTexture(GrContext*, const GrBackendTextureDesc&, - const SkSurfaceProps*); - // Legacy alias - static SkSurface* NewWrappedRenderTarget(GrContext* ctx, const GrBackendTextureDesc& desc, - const SkSurfaceProps* props) { - return NewFromBackendTexture(ctx, desc, props); - } - + static sk_sp MakeFromBackendTexture(GrContext*, const GrBackendTextureDesc&, + const SkSurfaceProps*); /** * Used to wrap a pre-existing 3D API rendering target as a SkSurface. Skia will not assume * ownership of the render target and the client must ensure the render target is valid for the * lifetime of the SkSurface. */ - static SkSurface* NewFromBackendRenderTarget(GrContext*, const GrBackendRenderTargetDesc&, - const SkSurfaceProps*); + static sk_sp MakeFromBackendRenderTarget(GrContext*, + const GrBackendRenderTargetDesc&, + const SkSurfaceProps*); + + /** + * Used to wrap a pre-existing 3D API texture as a SkSurface. Skia will treat the texture as + * a rendering target only, but unlike NewFromBackendRenderTarget, Skia will manage and own + * the associated render target objects (but not the provided texture). The kRenderTarget flag + * must be set on GrBackendTextureDesc for this to succeed. Skia will not assume ownership + * of the texture and the client must ensure the texture is valid for the lifetime of the + * SkSurface. + */ + static sk_sp MakeFromBackendTextureAsRenderTarget( + GrContext*, const GrBackendTextureDesc&, const SkSurfaceProps*); /** * Return a new surface whose contents will be drawn to an offscreen * render target, allocated by the surface. + * + * The GrTextureStorageAllocator will be reused if SkImage snapshots create + * additional textures. */ - static SkSurface* NewRenderTarget(GrContext*, Budgeted, const SkImageInfo&, int sampleCount, - const SkSurfaceProps* = NULL); + static sk_sp MakeRenderTarget( + GrContext*, SkBudgeted, const SkImageInfo&, int sampleCount, const SkSurfaceProps*, + GrTextureStorageAllocator = GrTextureStorageAllocator()); - static SkSurface* NewRenderTarget(GrContext* gr, Budgeted b, const SkImageInfo& info) { - return NewRenderTarget(gr, b, info, 0, NULL); + static sk_sp MakeRenderTarget(GrContext* gr, SkBudgeted b, const SkImageInfo& info) { + return MakeRenderTarget(gr, b, info, 0, nullptr); } +#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API + static SkSurface* NewRasterDirect(const SkImageInfo& info, void* pixels, size_t rowBytes, + const SkSurfaceProps* props = NULL) { + return MakeRasterDirect(info, pixels, rowBytes, props).release(); + } + static SkSurface* NewRasterDirectReleaseProc(const SkImageInfo& info, void* pixels, + size_t rowBytes, + void (*releaseProc)(void* pixels, void* context), + void* context, const SkSurfaceProps* props = NULL){ + return MakeRasterDirectReleaseProc(info, pixels, rowBytes, releaseProc, context, + props).release(); + } + static SkSurface* NewRaster(const SkImageInfo& info, size_t rowBytes, + const SkSurfaceProps* props) { + return MakeRaster(info, rowBytes, props).release(); + } + static SkSurface* NewRaster(const SkImageInfo& info, const SkSurfaceProps* props = NULL) { + return MakeRaster(info, props).release(); + } + static SkSurface* NewRasterN32Premul(int width, int height, + const SkSurfaceProps* props = NULL) { + return NewRaster(SkImageInfo::MakeN32Premul(width, height), props); + } + static SkSurface* NewRenderTargetDirect(GrRenderTarget* rt, const SkSurfaceProps* props) { + return MakeRenderTargetDirect(rt, props).release(); + } + static SkSurface* NewRenderTargetDirect(GrRenderTarget* target) { + return NewRenderTargetDirect(target, NULL); + } + static SkSurface* NewFromBackendTexture(GrContext* ctx, const GrBackendTextureDesc& desc, + const SkSurfaceProps* props) { + return MakeFromBackendTexture(ctx, desc, props).release(); + } + // Legacy alias + static SkSurface* NewWrappedRenderTarget(GrContext* ctx, const GrBackendTextureDesc& desc, + const SkSurfaceProps* props) { + return NewFromBackendTexture(ctx, desc, props); + } + static SkSurface* NewFromBackendRenderTarget(GrContext* ctx, const GrBackendRenderTargetDesc& d, + const SkSurfaceProps* props) { + return MakeFromBackendRenderTarget(ctx, d, props).release(); + } + static SkSurface* NewFromBackendTextureAsRenderTarget(GrContext* ctx, + const GrBackendTextureDesc& desc, + const SkSurfaceProps* props) { + return MakeFromBackendTextureAsRenderTarget(ctx, desc, props).release(); + } + static SkSurface* NewRenderTarget(GrContext* ctx, SkBudgeted b, const SkImageInfo& info, + int sampleCount, const SkSurfaceProps* props = NULL, + GrTextureStorageAllocator a = GrTextureStorageAllocator()) { + return MakeRenderTarget(ctx, b, info, sampleCount, props, a).release(); + } + static SkSurface* NewRenderTarget(GrContext* gr, SkBudgeted b, const SkImageInfo& info) { + return NewRenderTarget(gr, b, info, 0); + } + SkSurface* newSurface(const SkImageInfo& info) { return this->makeSurface(info).release(); } +#endif + int width() const { return fWidth; } int height() const { return fHeight; } @@ -214,7 +275,7 @@ public: * ... // draw using canvasB * canvasA->drawSurface(surfaceB); // <--- this will always be optimal! */ - SkSurface* newSurface(const SkImageInfo&); + sk_sp makeSurface(const SkImageInfo&); /** * Returns an image of the current state of the surface pixels up to this @@ -223,7 +284,28 @@ public: * parameter controls whether it counts against the resource budget * (currently for the gpu backend only). */ - SkImage* newImageSnapshot(Budgeted = kYes_Budgeted); + sk_sp makeImageSnapshot(SkBudgeted = SkBudgeted::kYes); + + /** + * In rare instances a client may want a unique copy of the SkSurface's contents in an image + * snapshot. This enum can be used to enforce that the image snapshot's backing store is not + * shared with another image snapshot or the surface's backing store. This is generally more + * expensive. This was added for Chromium bug 585250. + */ + enum ForceUnique { + kNo_ForceUnique, + kYes_ForceUnique + }; + sk_sp makeImageSnapshot(SkBudgeted, ForceUnique); + +#ifdef SK_SUPPORT_LEGACY_IMAGEFACTORY + SkImage* newImageSnapshot(SkBudgeted budgeted = SkBudgeted::kYes) { + return this->makeImageSnapshot(budgeted).release(); + } + SkImage* newImageSnapshot(SkBudgeted budgeted, ForceUnique force) { + return this->makeImageSnapshot(budgeted, force).release(); + } +#endif /** * Though the caller could get a snapshot image explicitly, and draw that, @@ -236,15 +318,18 @@ public: /** * If the surface has direct access to its pixels (i.e. they are in local - * RAM) return the const-address of those pixels, and if not null, return - * the ImageInfo and rowBytes. The returned address is only valid while + * RAM) return true, and if not null, set the pixmap parameter to point to the information + * about the surface's pixels. The pixel address in the pixmap is only valid while * the surface object is in scope, and no API call is made on the surface * or its canvas. * - * On failure, returns NULL and the info and rowBytes parameters are - * ignored. + * On failure, returns false and the pixmap parameter is ignored. */ + bool peekPixels(SkPixmap*); + +#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS const void* peekPixels(SkImageInfo* info, size_t* rowBytes); +#endif /** * Copy the pixels from the surface into the specified buffer (pixels + rowBytes), @@ -269,6 +354,11 @@ public: const SkSurfaceProps& props() const { return fProps; } + /** + * Issue any pending surface IO to the current backend 3D API and resolve any surface MSAA. + */ + void prepareForExternalIO(); + protected: SkSurface(int width, int height, const SkSurfaceProps*); SkSurface(const SkImageInfo&, const SkSurfaceProps*); diff --git a/gfx/skia/skia/include/core/SkSurfaceProps.h b/gfx/skia/skia/include/core/SkSurfaceProps.h index 735561f1dc..bd4fa8e7fc 100644 --- a/gfx/skia/skia/include/core/SkSurfaceProps.h +++ b/gfx/skia/skia/include/core/SkSurfaceProps.h @@ -54,6 +54,17 @@ public: kDisallowAntiAlias_Flag = 1 << 0, kDisallowDither_Flag = 1 << 1, kUseDeviceIndependentFonts_Flag = 1 << 2, + + /** + * This flag causes sRGB inputs to the color pipeline (images and other sRGB-tagged + * colors) to be gamma-corrected (converted to linear) before use. Without this flag, + * texture scaling and filtering is not gamma correct, preserving the behavior of Skia + * up through 2015. + * + * It is recommended to enable this flag when rendering to an sRGB or floating point + * surface. + */ + kAllowSRGBInputs_Flag = 1 << 3, }; /** Deprecated alias used by Chromium. Will be removed. */ static const Flags kUseDistanceFieldFonts_Flag = kUseDeviceIndependentFonts_Flag; @@ -75,6 +86,7 @@ public: bool isUseDeviceIndependentFonts() const { return SkToBool(fFlags & kUseDeviceIndependentFonts_Flag); } + bool allowSRGBInputs() const { return SkToBool(fFlags & kAllowSRGBInputs_Flag); } private: SkSurfaceProps(); diff --git a/gfx/skia/skia/include/core/SkSwizzle.h b/gfx/skia/skia/include/core/SkSwizzle.h new file mode 100644 index 0000000000..253f4e39a0 --- /dev/null +++ b/gfx/skia/skia/include/core/SkSwizzle.h @@ -0,0 +1,19 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkSwizzle_DEFINED +#define SkSwizzle_DEFINED + +#include "SkTypes.h" + +/** + Swizzles byte order of |count| 32-bit pixels, swapping R and B. + (RGBA <-> BGRA) +*/ +SK_API void SkSwapRB(uint32_t* dest, const uint32_t* src, int count); + +#endif diff --git a/gfx/skia/skia/include/core/SkTLazy.h b/gfx/skia/skia/include/core/SkTLazy.h index 8032538c44..399b26cde8 100644 --- a/gfx/skia/skia/include/core/SkTLazy.h +++ b/gfx/skia/skia/include/core/SkTLazy.h @@ -29,7 +29,7 @@ public: SkTLazy(const SkTLazy& src) : fPtr(NULL) { if (src.isValid()) { - fPtr = new (fStorage.get()) T(*src->get()); + fPtr = new (fStorage.get()) T(*src.get()); } else { fPtr = NULL; } diff --git a/gfx/skia/skia/include/core/SkTextBlob.h b/gfx/skia/skia/include/core/SkTextBlob.h index b0ab8929e6..e43ff74a85 100644 --- a/gfx/skia/skia/include/core/SkTextBlob.h +++ b/gfx/skia/skia/include/core/SkTextBlob.h @@ -8,10 +8,9 @@ #ifndef SkTextBlob_DEFINED #define SkTextBlob_DEFINED +#include "../private/SkTemplates.h" #include "SkPaint.h" #include "SkRefCnt.h" -#include "SkTArray.h" -#include "SkTDArray.h" class SkReadBuffer; class SkWriteBuffer; diff --git a/gfx/skia/skia/include/core/SkTime.h b/gfx/skia/skia/include/core/SkTime.h index 3ff29aa25e..8a8224a82a 100644 --- a/gfx/skia/skia/include/core/SkTime.h +++ b/gfx/skia/skia/include/core/SkTime.h @@ -34,34 +34,27 @@ public: }; static void GetDateTime(DateTime*); - static SkMSec GetMSecs() { return (SkMSec)(GetNSecs() * 1e-6); } + static double GetSecs() { return GetNSecs() * 1e-9; } + static double GetMSecs() { return GetNSecs() * 1e-6; } static double GetNSecs(); }; -#define SK_TIME_FACTOR 1 - /////////////////////////////////////////////////////////////////////////////// class SkAutoTime { public: // The label is not deep-copied, so its address must remain valid for the // lifetime of this object - SkAutoTime(const char* label = NULL, SkMSec minToDump = 0) : fLabel(label) - { - fNow = SkTime::GetMSecs(); - fMinToDump = minToDump; - } - ~SkAutoTime() - { - SkMSec dur = SkTime::GetMSecs() - fNow; - if (dur >= fMinToDump) { - SkDebugf("%s %d\n", fLabel ? fLabel : "", dur); - } + SkAutoTime(const char* label = nullptr) + : fLabel(label) + , fNow(SkTime::GetMSecs()) {} + ~SkAutoTime() { + uint64_t dur = static_cast(SkTime::GetMSecs() - fNow); + SkDebugf("%s %ld\n", fLabel ? fLabel : "", dur); } private: const char* fLabel; - SkMSec fNow; - SkMSec fMinToDump; + double fNow; }; #define SkAutoTime(...) SK_REQUIRE_LOCAL_VAR(SkAutoTime) diff --git a/gfx/skia/skia/include/core/SkTypeface.h b/gfx/skia/skia/include/core/SkTypeface.h index 0b1ca6a4bd..80aa5fe03d 100644 --- a/gfx/skia/skia/include/core/SkTypeface.h +++ b/gfx/skia/skia/include/core/SkTypeface.h @@ -305,6 +305,12 @@ public: */ SkRect getBounds() const; + /*** + * Returns whether this typeface has color glyphs and therefore cannot be + * rendered as a path. e.g. Emojis. + */ + virtual bool hasColorGlyphs() const { return false; } + // PRIVATE / EXPERIMENTAL -- do not call void filterRec(SkScalerContextRec* rec) const { this->onFilterRec(rec); diff --git a/gfx/skia/skia/include/core/SkTypes.h b/gfx/skia/skia/include/core/SkTypes.h index c4361ace04..6aa4ce4e21 100644 --- a/gfx/skia/skia/include/core/SkTypes.h +++ b/gfx/skia/skia/include/core/SkTypes.h @@ -67,7 +67,7 @@ SK_API extern void sk_out_of_memory(void); The platform implementation must not return, but should either throw an exception or otherwise exit. */ -SK_API extern void sk_throw(void); +SK_API extern void sk_abort_no_print(void); enum { SK_MALLOC_TEMP = 0x01, //!< hint to sk_malloc that the requested memory will be freed in the scope of the stack frame @@ -133,8 +133,10 @@ inline void operator delete(void* p) { SK_API void SkDebugf(const char format[], ...); #endif +#define SkASSERT_RELEASE(cond) if(!(cond)) { SK_ABORT(#cond); } + #ifdef SK_DEBUG - #define SkASSERT(cond) SK_ALWAYSBREAK(cond) + #define SkASSERT(cond) SkASSERT_RELEASE(cond) #define SkDEBUGFAIL(message) SkASSERT(false && message) #define SkDEBUGFAILF(fmt, ...) SkASSERTF(false, fmt, ##__VA_ARGS__) #define SkDEBUGCODE(code) code @@ -155,7 +157,9 @@ inline void operator delete(void* p) { #define SkAssertResult(cond) cond #endif -#define SkFAIL(message) SK_ALWAYSBREAK(false && message) +// Legacy macro names for SK_ABORT +#define SkFAIL(message) SK_ABORT(message) +#define sk_throw() SK_ABORT("sk_throw") // We want to evaluate cond only once, and inside the SkASSERT somewhere so we see its string form. // So we use the comma operator to make an SkDebugf that always returns false: we'll evaluate cond, @@ -257,27 +261,20 @@ typedef unsigned U16CPU; */ typedef uint8_t SkBool8; -#ifdef SK_DEBUG - SK_API int8_t SkToS8(intmax_t); - SK_API uint8_t SkToU8(uintmax_t); - SK_API int16_t SkToS16(intmax_t); - SK_API uint16_t SkToU16(uintmax_t); - SK_API int32_t SkToS32(intmax_t); - SK_API uint32_t SkToU32(uintmax_t); - SK_API int SkToInt(intmax_t); - SK_API unsigned SkToUInt(uintmax_t); - SK_API size_t SkToSizeT(uintmax_t); -#else - #define SkToS8(x) ((int8_t)(x)) - #define SkToU8(x) ((uint8_t)(x)) - #define SkToS16(x) ((int16_t)(x)) - #define SkToU16(x) ((uint16_t)(x)) - #define SkToS32(x) ((int32_t)(x)) - #define SkToU32(x) ((uint32_t)(x)) - #define SkToInt(x) ((int)(x)) - #define SkToUInt(x) ((unsigned)(x)) - #define SkToSizeT(x) ((size_t)(x)) -#endif +#include "../private/SkTFitsIn.h" +template D SkTo(S s) { + SkASSERT(SkTFitsIn(s)); + return static_cast(s); +} +#define SkToS8(x) SkTo(x) +#define SkToU8(x) SkTo(x) +#define SkToS16(x) SkTo(x) +#define SkToU16(x) SkTo(x) +#define SkToS32(x) SkTo(x) +#define SkToU32(x) SkTo(x) +#define SkToInt(x) SkTo(x) +#define SkToUInt(x) SkTo(x) +#define SkToSizeT(x) SkTo(x) /** Returns 0 or 1 based on the condition */ @@ -339,6 +336,9 @@ template char (&SkArrayCountHelper(T (&array)[N]))[N]; #define SkAlign8(x) (((x) + 7) >> 3 << 3) #define SkIsAlign8(x) (0 == ((x) & 7)) +#define SkAlign16(x) (((x) + 15) >> 4 << 4) +#define SkIsAlign16(x) (0 == ((x) & 15)) + #define SkAlignPtr(x) (sizeof(void*) == 8 ? SkAlign8(x) : SkAlign4(x)) #define SkIsAlignPtr(x) (sizeof(void*) == 8 ? SkIsAlign8(x) : SkIsAlign4(x)) @@ -348,13 +348,15 @@ typedef uint32_t SkFourByteTag; /** 32 bit integer to hold a unicode value */ typedef int32_t SkUnichar; -/** 32 bit value to hold a millisecond count -*/ + +/** 32 bit value to hold a millisecond duration + * Note that SK_MSecMax is about 25 days. + */ typedef uint32_t SkMSec; /** 1 second measured in milliseconds */ #define SK_MSec1 1000 -/** maximum representable milliseconds +/** maximum representable milliseconds; 24d 20h 31m 23.647s. */ #define SK_MSecMax 0x7FFFFFFF /** Returns a < b for milliseconds, correctly handling wrap-around from 0xFFFFFFFF to 0 @@ -441,6 +443,17 @@ template static inline const T& SkTPin(const T& value, const T& min return SkTMax(SkTMin(value, max), min); } + +/////////////////////////////////////////////////////////////////////////////// + +/** + * Indicates whether an allocation should count against a cache budget. + */ +enum class SkBudgeted : bool { + kNo = false, + kYes = true +}; + /////////////////////////////////////////////////////////////////////////////// /** Use to combine multiple bits in a bitmask in a type safe way. @@ -502,12 +515,12 @@ public: internal reference to null. Note the caller is reponsible for calling sk_free on the returned address. */ - void* detach() { return this->set(NULL); } + void* release() { return this->set(NULL); } /** Free the current buffer, and set the internal reference to NULL. Same - as calling sk_free(detach()) + as calling sk_free(release()) */ - void free() { + void reset() { sk_free(fPtr); fPtr = NULL; } @@ -523,7 +536,7 @@ private: /** * Manage an allocated block of heap memory. This object is the sole manager of * the lifetime of the block, so the caller must not call sk_free() or delete - * on the block, unless detach() was called. + * on the block, unless release() was called. */ class SkAutoMalloc : SkNoncopyable { public: @@ -559,7 +572,7 @@ public: /** * Reallocates the block to a new size. The ptr may or may not change. */ - void* reset(size_t size, OnShrink shrink = kAlloc_OnShrink, bool* didChangeAlloc = NULL) { + void* reset(size_t size = 0, OnShrink shrink = kAlloc_OnShrink, bool* didChangeAlloc = NULL) { if (size == fSize || (kReuse_OnShrink == shrink && size < fSize)) { if (didChangeAlloc) { *didChangeAlloc = false; @@ -577,13 +590,6 @@ public: return fPtr; } - /** - * Releases the block back to the heap - */ - void free() { - this->reset(0); - } - /** * Return the allocated block. */ @@ -594,7 +600,7 @@ public: internal reference to null. Note the caller is reponsible for calling sk_free on the returned address. */ - void* detach() { + void* release() { void* ptr = fPtr; fPtr = NULL; fSize = 0; @@ -608,17 +614,16 @@ private: #define SkAutoMalloc(...) SK_REQUIRE_LOCAL_VAR(SkAutoMalloc) /** - * Manage an allocated block of memory. If the requested size is <= kSize, then - * the allocation will come from the stack rather than the heap. This object - * is the sole manager of the lifetime of the block, so the caller must not - * call sk_free() or delete on the block. + * Manage an allocated block of memory. If the requested size is <= kSizeRequested (or slightly + * more), then the allocation will come from the stack rather than the heap. This object is the + * sole manager of the lifetime of the block, so the caller must not call sk_free() or delete on + * the block. */ -template class SkAutoSMalloc : SkNoncopyable { +template class SkAutoSMalloc : SkNoncopyable { public: /** - * Creates initially empty storage. get() returns a ptr, but it is to - * a zero-byte allocation. Must call reset(size) to return an allocated - * block. + * Creates initially empty storage. get() returns a ptr, but it is to a zero-byte allocation. + * Must call reset(size) to return an allocated block. */ SkAutoSMalloc() { fPtr = fStorage; @@ -626,9 +631,8 @@ public: } /** - * Allocate a block of the specified size. If size <= kSize, then the - * allocation will come from the stack, otherwise it will be dynamically - * allocated. + * Allocate a block of the specified size. If size <= kSizeRequested (or slightly more), then + * the allocation will come from the stack, otherwise it will be dynamically allocated. */ explicit SkAutoSMalloc(size_t size) { fPtr = fStorage; @@ -637,8 +641,8 @@ public: } /** - * Free the allocated block (if any). If the block was small enought to - * have been allocated on the stack (size <= kSize) then this does nothing. + * Free the allocated block (if any). If the block was small enough to have been allocated on + * the stack, then this does nothing. */ ~SkAutoSMalloc() { if (fPtr != (void*)fStorage) { @@ -647,18 +651,16 @@ public: } /** - * Return the allocated block. May return non-null even if the block is - * of zero size. Since this may be on the stack or dynamically allocated, - * the caller must not call sk_free() on it, but must rely on SkAutoSMalloc - * to manage it. + * Return the allocated block. May return non-null even if the block is of zero size. Since + * this may be on the stack or dynamically allocated, the caller must not call sk_free() on it, + * but must rely on SkAutoSMalloc to manage it. */ void* get() const { return fPtr; } /** - * Return a new block of the requested size, freeing (as necessary) any - * previously allocated block. As with the constructor, if size <= kSize - * then the return block may be allocated locally, rather than from the - * heap. + * Return a new block of the requested size, freeing (as necessary) any previously allocated + * block. As with the constructor, if size <= kSizeRequested (or slightly more) then the return + * block may be allocated locally, rather than from the heap. */ void* reset(size_t size, SkAutoMalloc::OnShrink shrink = SkAutoMalloc::kAlloc_OnShrink, @@ -688,9 +690,20 @@ public: } private: + // Align up to 32 bits. + static const size_t kSizeAlign4 = SkAlign4(kSizeRequested); +#if defined(GOOGLE3) + // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions + // have multiple large stack allocations. + static const size_t kMaxBytes = 4 * 1024; + static const size_t kSize = kSizeRequested > kMaxBytes ? kMaxBytes : kSizeAlign4; +#else + static const size_t kSize = kSizeAlign4; +#endif + void* fPtr; size_t fSize; // can be larger than the requested size (see kReuse) - uint32_t fStorage[(kSize + 3) >> 2]; + uint32_t fStorage[kSize >> 2]; }; // Can't guard the constructor because it's a template class. diff --git a/gfx/skia/skia/include/core/SkWriteBuffer.h b/gfx/skia/skia/include/core/SkWriteBuffer.h index 8e4607887d..3ae82d014a 100644 --- a/gfx/skia/skia/include/core/SkWriteBuffer.h +++ b/gfx/skia/skia/include/core/SkWriteBuffer.h @@ -10,6 +10,7 @@ #define SkWriteBuffer_DEFINED #include "SkData.h" +#include "SkImage.h" #include "SkPath.h" #include "SkPicture.h" #include "SkPixelSerializer.h" @@ -50,7 +51,6 @@ public: void writeByteArray(const void* data, size_t size); void writeDataAsByteArray(SkData* data) { this->writeByteArray(data->data(), data->size()); } void writeBool(bool value); - void writeFixed(SkFixed value); void writeScalar(SkScalar value); void writeScalarArray(const SkScalar* value, uint32_t count); void writeInt(int32_t value); diff --git a/gfx/skia/skia/include/core/SkWriter32.h b/gfx/skia/skia/include/core/SkWriter32.h index 1e7ec6d348..26388cd37e 100644 --- a/gfx/skia/skia/include/core/SkWriter32.h +++ b/gfx/skia/skia/include/core/SkWriter32.h @@ -206,6 +206,18 @@ public: */ static size_t WriteStringSize(const char* str, size_t len = (size_t)-1); + void writeData(const SkData* data) { + uint32_t len = data ? SkToU32(data->size()) : 0; + this->write32(len); + if (data) { + this->writePad(data->data(), len); + } + } + + static size_t WriteDataSize(const SkData* data) { + return 4 + SkAlign4(data ? data->size() : 0); + } + /** * Move the cursor back to offset bytes from the beginning. * offset must be a multiple of 4 no greater than size(). @@ -234,7 +246,7 @@ public: /** * Captures a snapshot of the data as it is right now, and return it. */ - SkData* snapshotAsData() const; + sk_sp snapshotAsData() const; private: void growToAtLeast(size_t size); diff --git a/gfx/skia/skia/include/core/SkXfermode.h b/gfx/skia/skia/include/core/SkXfermode.h index 91268ab5f1..db8d570ce0 100644 --- a/gfx/skia/skia/include/core/SkXfermode.h +++ b/gfx/skia/skia/include/core/SkXfermode.h @@ -18,6 +18,9 @@ class GrTexture; class GrXPFactory; class SkString; +struct SkPM4f; +typedef SkPM4f (*SkXfermodeProc4f)(const SkPM4f& src, const SkPM4f& dst); + /** \class SkXfermode * * SkXfermode is the base class for objects that are called to implement custom @@ -123,6 +126,9 @@ public: * if the xfermode is NULL, and if so, treats it as kSrcOver_Mode. */ static bool AsMode(const SkXfermode*, Mode* mode); + static bool AsMode(const sk_sp& xfer, Mode* mode) { + return AsMode(xfer.get(), mode); + } /** * Returns true if the xfermode claims to be the specified Mode. This works @@ -135,15 +141,30 @@ public: * } */ static bool IsMode(const SkXfermode* xfer, Mode mode); + static bool IsMode(const sk_sp& xfer, Mode mode) { + return IsMode(xfer.get(), mode); + } /** Return an SkXfermode object for the specified mode. */ - static SkXfermode* Create(Mode mode); + static sk_sp Make(Mode); +#ifdef SK_SUPPORT_LEGACY_XFERMODE_PTR + static SkXfermode* Create(Mode mode) { + return Make(mode).release(); + } + SK_ATTR_DEPRECATED("use AsMode(...)") + static bool IsMode(const SkXfermode* xfer, Mode* mode) { + return AsMode(xfer, mode); + } +#endif /** Return a function pointer to a routine that applies the specified porter-duff transfer mode. */ static SkXfermodeProc GetProc(Mode mode); + static SkXfermodeProc4f GetProc4f(Mode); + + virtual SkXfermodeProc4f getProc4f() const; /** * If the specified mode can be represented by a pair of Coeff, then return @@ -153,11 +174,6 @@ public: */ static bool ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst); - SK_ATTR_DEPRECATED("use AsMode(...)") - static bool IsMode(const SkXfermode* xfer, Mode* mode) { - return AsMode(xfer, mode); - } - /** * Returns whether or not the xfer mode can support treating coverage as alpha */ @@ -168,6 +184,9 @@ public: * the xfermode is NULL, and if so, treats it as kSrcOver_Mode. */ static bool SupportsCoverageAsAlpha(const SkXfermode* xfer); + static bool SupportsCoverageAsAlpha(const sk_sp& xfer) { + return SupportsCoverageAsAlpha(xfer.get()); + } enum SrcColorOpacity { // The src color is known to be opaque (alpha == 255) @@ -192,43 +211,65 @@ public: * the xfermode is NULL, and if so, treats it as kSrcOver_Mode. */ static bool IsOpaque(const SkXfermode* xfer, SrcColorOpacity opacityType); + static bool IsOpaque(const sk_sp& xfer, SrcColorOpacity opacityType) { + return IsOpaque(xfer.get(), opacityType); + } - /** Used to do in-shader blending between two colors computed in the shader via a - GrFragmentProcessor. The input to the returned FP is the src color. The dst color is - provided by the dst param which becomes a child FP of the returned FP. If the params are - null then this is just a query of whether the SkXfermode could support this functionality. - It is legal for the function to succeed but return a null output. This indicates that +#if SK_SUPPORT_GPU + /** Used by the SkXfermodeImageFilter to blend two colors via a GrFragmentProcessor. + The input to the returned FP is the src color. The dst color is + provided by the dst param which becomes a child FP of the returned FP. + It is legal for the function to return a null output. This indicates that the output of the blend is simply the src color. */ - virtual bool asFragmentProcessor(const GrFragmentProcessor** output, - const GrFragmentProcessor* dst) const; + virtual const GrFragmentProcessor* getFragmentProcessorForImageFilter( + const GrFragmentProcessor* dst) const; - /** A subclass may implement this factory function to work with the GPU backend. It is legal - to call this with xpf NULL to simply test the return value. If xpf is non-NULL then the - xfermode may optionally allocate a factory to return to the caller as *xpf. The caller - will install it and own a ref to it. Since the xfermode may or may not assign *xpf, the - caller should set *xpf to NULL beforehand. XferProcessors cannot use a background texture. + /** A subclass must implement this factory function to work with the GPU backend. + The xfermode will return a factory for which the caller will get a ref. It is up + to the caller to install it. XferProcessors cannot use a background texture. */ - virtual bool asXPFactory(GrXPFactory** xpf) const; - - /** Returns true if the xfermode can be expressed as an xfer processor factory (xpFactory). - This helper calls the asXPFactory() virtual. If the xfermode is NULL, it is treated as - kSrcOver_Mode. It is legal to call this with xpf param NULL to simply test the return value. - */ - static inline bool AsXPFactory(SkXfermode* xfermode, GrXPFactory** xpf) { - if (nullptr == xfermode) { - if (xpf) { - *xpf = nullptr; - } - return true; - } - return xfermode->asXPFactory(xpf); - } + virtual GrXPFactory* asXPFactory() const; +#endif SK_TO_STRING_PUREVIRT() SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() SK_DEFINE_FLATTENABLE_TYPE(SkXfermode) + enum D32Flags { + kSrcIsOpaque_D32Flag = 1 << 0, + kSrcIsSingle_D32Flag = 1 << 1, + kDstIsSRGB_D32Flag = 1 << 2, + }; + typedef void (*D32Proc)(const SkXfermode*, uint32_t dst[], const SkPM4f src[], + int count, const SkAlpha coverage[]); + static D32Proc GetD32Proc(SkXfermode*, uint32_t flags); + static D32Proc GetD32Proc(const sk_sp& xfer, uint32_t flags) { + return GetD32Proc(xfer.get(), flags); + } + + enum D64Flags { + kSrcIsOpaque_D64Flag = 1 << 0, + kSrcIsSingle_D64Flag = 1 << 1, + kDstIsFloat16_D64Flag = 1 << 2, // else U16 bit components + }; + typedef void (*D64Proc)(const SkXfermode*, uint64_t dst[], const SkPM4f src[], int count, + const SkAlpha coverage[]); + static D64Proc GetD64Proc(SkXfermode*, uint32_t flags); + static D64Proc GetD64Proc(const sk_sp& xfer, uint32_t flags) { + return GetD64Proc(xfer.get(), flags); + } + + enum LCDFlags { + kSrcIsOpaque_LCDFlag = 1 << 0, // else src(s) may have alpha < 1 + kSrcIsSingle_LCDFlag = 1 << 1, // else src[count] + kDstIsLinearInt_LCDFlag = 1 << 2, // else srgb/half-float + }; + typedef void (*LCD32Proc)(uint32_t* dst, const SkPM4f* src, int count, const uint16_t lcd[]); + typedef void (*LCD64Proc)(uint64_t* dst, const SkPM4f* src, int count, const uint16_t lcd[]); + static LCD32Proc GetLCD32Proc(uint32_t flags); + static LCD64Proc GetLCD64Proc(uint32_t) { return nullptr; } + protected: SkXfermode() {} /** The default implementation of xfer32/xfer16/xferA8 in turn call this @@ -241,6 +282,9 @@ protected: */ virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst) const; + virtual D32Proc onGetD32Proc(uint32_t flags) const; + virtual D64Proc onGetD64Proc(uint32_t flags) const; + private: enum { kModeCount = kLastMode + 1 diff --git a/gfx/skia/skia/include/core/SkYUVSizeInfo.h b/gfx/skia/skia/include/core/SkYUVSizeInfo.h new file mode 100644 index 0000000000..2c5a51d794 --- /dev/null +++ b/gfx/skia/skia/include/core/SkYUVSizeInfo.h @@ -0,0 +1,34 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkYUVSizeInfo_DEFINED +#define SkYUVSizeInfo_DEFINED + +struct SkYUVSizeInfo { + enum { + kY = 0, + kU = 1, + kV = 2, + }; + SkISize fSizes[3]; + + /** + * While the widths of the Y, U, and V planes are not restricted, the + * implementation often requires that the width of the memory allocated + * for each plane be a multiple of 8. + * + * This struct allows us to inform the client how many "widthBytes" + * that we need. Note that we use the new idea of "widthBytes" + * because this idea is distinct from "rowBytes" (used elsewhere in + * Skia). "rowBytes" allow the last row of the allocation to not + * include any extra padding, while, in this case, every single row of + * the allocation must be at least "widthBytes". + */ + size_t fWidthBytes[3]; +}; + +#endif // SkYUVSizeInfo_DEFINED diff --git a/gfx/skia/skia/include/effects/Sk1DPathEffect.h b/gfx/skia/skia/include/effects/Sk1DPathEffect.h index 3419dc23b7..d5315a8735 100644 --- a/gfx/skia/skia/include/effects/Sk1DPathEffect.h +++ b/gfx/skia/skia/include/effects/Sk1DPathEffect.h @@ -46,7 +46,7 @@ public: kRotate_Style, // rotate the shape about its center kMorph_Style, // transform each point, and turn lines into curves - kStyleCount + kLastEnum_Style = kMorph_Style, }; /** Dash by replicating the specified path. @@ -56,9 +56,13 @@ public: @param style how to transform path at each point (based on the current position and tangent) */ - static SkPathEffect* Create(const SkPath& path, SkScalar advance, SkScalar phase, Style style) { - return new SkPath1DPathEffect(path, advance, phase, style); + static sk_sp Make(const SkPath& path, SkScalar advance, SkScalar phase, Style); + +#ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR + static SkPathEffect* Create(const SkPath& path, SkScalar advance, SkScalar phase, Style s) { + return Make(path, advance, phase, s).release(); } +#endif virtual bool filterPath(SkPath*, const SkPath&, SkStrokeRec*, const SkRect*) const override; diff --git a/gfx/skia/skia/include/effects/Sk2DPathEffect.h b/gfx/skia/skia/include/effects/Sk2DPathEffect.h index 73da83c4b9..823a6ad9d8 100644 --- a/gfx/skia/skia/include/effects/Sk2DPathEffect.h +++ b/gfx/skia/skia/include/effects/Sk2DPathEffect.h @@ -55,8 +55,8 @@ private: class SK_API SkLine2DPathEffect : public Sk2DPathEffect { public: - static SkPathEffect* Create(SkScalar width, const SkMatrix& matrix) { - return new SkLine2DPathEffect(width, matrix); + static sk_sp Make(SkScalar width, const SkMatrix& matrix) { + return sk_sp(new SkLine2DPathEffect(width, matrix)); } virtual bool filterPath(SkPath* dst, const SkPath& src, @@ -84,8 +84,8 @@ public: * Stamp the specified path to fill the shape, using the matrix to define * the latice. */ - static SkPathEffect* Create(const SkMatrix& matrix, const SkPath& path) { - return new SkPath2DPathEffect(matrix, path); + static sk_sp Make(const SkMatrix& matrix, const SkPath& path) { + return sk_sp(new SkPath2DPathEffect(matrix, path)); } SK_TO_STRING_OVERRIDE() diff --git a/gfx/skia/skia/include/effects/SkAlphaThresholdFilter.h b/gfx/skia/skia/include/effects/SkAlphaThresholdFilter.h index 17521b6455..aee365f999 100644 --- a/gfx/skia/skia/include/effects/SkAlphaThresholdFilter.h +++ b/gfx/skia/skia/include/effects/SkAlphaThresholdFilter.h @@ -8,8 +8,8 @@ #ifndef SkAlphaThresholdFilter_DEFINED #define SkAlphaThresholdFilter_DEFINED -#include "SkRegion.h" #include "SkImageFilter.h" +#include "SkRegion.h" class SK_API SkAlphaThresholdFilter { public: @@ -20,8 +20,17 @@ public: * The 0,0 point of the region corresponds to the upper left corner of the * source image. */ - static SkImageFilter* Create(const SkRegion& region, SkScalar innerThreshold, - SkScalar outerThreshold, SkImageFilter* input = NULL); + static sk_sp Make(const SkRegion& region, SkScalar innerMin, + SkScalar outerMax, sk_sp input); + + +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(const SkRegion& region, SkScalar innerMin, + SkScalar outerMax, SkImageFilter* input = nullptr) { + return Make(region, innerMin, outerMax, sk_ref_sp(input)).release(); + } +#endif + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP(); }; diff --git a/gfx/skia/skia/include/effects/SkArcToPathEffect.h b/gfx/skia/skia/include/effects/SkArcToPathEffect.h index 4716ea125d..fcf4a3a5dc 100644 --- a/gfx/skia/skia/include/effects/SkArcToPathEffect.h +++ b/gfx/skia/skia/include/effects/SkArcToPathEffect.h @@ -15,11 +15,11 @@ public: /** radius must be > 0 to have an effect. It specifies the distance from each corner that should be "rounded". */ - static SkPathEffect* Create(SkScalar radius) { + static sk_sp Make(SkScalar radius) { if (radius <= 0) { return NULL; } - return new SkArcToPathEffect(radius); + return sk_sp(new SkArcToPathEffect(radius)); } bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override; diff --git a/gfx/skia/skia/include/effects/SkArithmeticMode.h b/gfx/skia/skia/include/effects/SkArithmeticMode.h index 3b9585d9ba..b160dc7e9b 100644 --- a/gfx/skia/skia/include/effects/SkArithmeticMode.h +++ b/gfx/skia/skia/include/effects/SkArithmeticMode.h @@ -10,24 +10,26 @@ #include "SkFlattenable.h" #include "SkScalar.h" - -class SkXfermode; +#include "SkXfermode.h" class SK_API SkArithmeticMode { public: /** * result = clamp[k1 * src * dst + k2 * src + k3 * dst + k4] * - * src and dst are treated as being [0.0 .. 1.0]. The polynomial is - * evaluated on their unpremultiplied components. - * * k1=k2=k3=0, k4=1.0 results in returning opaque white * k1=k3=k4=0, k2=1.0 results in returning the src * k1=k2=k4=0, k3=1.0 results in returning the dst */ + static sk_sp Make(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, + bool enforcePMColor = true); +#ifdef SK_SUPPORT_LEGACY_XFERMODE_PTR static SkXfermode* Create(SkScalar k1, SkScalar k2, SkScalar k3, SkScalar k4, - bool enforcePMColor = true); + bool enforcePMColor = true) { + return Make(k1, k2, k3, k4, enforcePMColor).release(); + } +#endif SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP(); diff --git a/gfx/skia/skia/include/effects/SkBlurDrawLooper.h b/gfx/skia/skia/include/effects/SkBlurDrawLooper.h index 930af15ff8..9b87683f04 100644 --- a/gfx/skia/skia/include/effects/SkBlurDrawLooper.h +++ b/gfx/skia/skia/include/effects/SkBlurDrawLooper.h @@ -35,12 +35,16 @@ public: kAll_BlurFlag = 0x07 }; + static sk_sp Make(SkColor color, SkScalar sigma, SkScalar dx, SkScalar dy, + uint32_t flags = kNone_BlurFlag) { + return sk_sp(new SkBlurDrawLooper(color, sigma, dx, dy, flags)); + } +#ifdef SK_SUPPORT_LEGACY_MINOR_EFFECT_PTR static SkDrawLooper* Create(SkColor color, SkScalar sigma, SkScalar dx, SkScalar dy, uint32_t flags = kNone_BlurFlag) { - return new SkBlurDrawLooper(color, sigma, dx, dy, flags); + return Make(color, sigma, dx, dy, flags).release(); } - - virtual ~SkBlurDrawLooper(); +#endif SkDrawLooper::Context* createContext(SkCanvas*, void* storage) const override; @@ -58,8 +62,8 @@ protected: bool asABlurShadow(BlurShadowRec*) const override; private: - SkMaskFilter* fBlur; - SkColorFilter* fColorFilter; + sk_sp fBlur; + sk_sp fColorFilter; SkScalar fDx, fDy, fSigma; SkColor fBlurColor; uint32_t fBlurFlags; diff --git a/gfx/skia/skia/include/effects/SkBlurImageFilter.h b/gfx/skia/skia/include/effects/SkBlurImageFilter.h index 5ae013d78e..607e76de51 100644 --- a/gfx/skia/skia/include/effects/SkBlurImageFilter.h +++ b/gfx/skia/skia/include/effects/SkBlurImageFilter.h @@ -13,33 +13,36 @@ class SK_API SkBlurImageFilter : public SkImageFilter { public: - static SkImageFilter* Create(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input = NULL, - const CropRect* cropRect = NULL) { + static sk_sp Make(SkScalar sigmaX, SkScalar sigmaY, sk_sp input, + const CropRect* cropRect = nullptr) { if (0 == sigmaX && 0 == sigmaY && nullptr == cropRect) { - return SkSafeRef(input); + return input; } - return new SkBlurImageFilter(sigmaX, sigmaY, input, cropRect); + return sk_sp(new SkBlurImageFilter(sigmaX, sigmaY, input, cropRect)); } - void computeFastBounds(const SkRect&, SkRect*) const override; + SkRect computeFastBounds(const SkRect&) const override; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input = nullptr, + const CropRect* cropRect = nullptr) { + return Make(sigmaX, sigmaY, sk_ref_sp(input), cropRect).release(); + } +#endif + protected: void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, - SkIPoint* offset) const override; - void onFilterNodeBounds(const SkIRect& src, const SkMatrix&, - SkIRect* dst, MapDirection) const override; - bool canFilterImageGPU() const override { return true; } - bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, - SkIPoint* offset) const override; + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; + SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override; private: SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, - SkImageFilter* input, + sk_sp input, const CropRect* cropRect); SkSize fSigma; diff --git a/gfx/skia/skia/include/effects/SkBlurMaskFilter.h b/gfx/skia/skia/include/effects/SkBlurMaskFilter.h index 356475ed4c..3ba91774f0 100644 --- a/gfx/skia/skia/include/effects/SkBlurMaskFilter.h +++ b/gfx/skia/skia/include/effects/SkBlurMaskFilter.h @@ -37,7 +37,8 @@ public: * @param flags Flags to use - defaults to none * @return The new blur maskfilter */ - static SkMaskFilter* Create(SkBlurStyle style, SkScalar sigma, uint32_t flags = kNone_BlurFlag); + static sk_sp Make(SkBlurStyle style, SkScalar sigma, + uint32_t flags = kNone_BlurFlag); /** Create an emboss maskfilter @param blurSigma standard deviation of the Gaussian blur to apply @@ -47,13 +48,22 @@ public: @param specular coefficient for specular highlights (e.g. 8) @return the emboss maskfilter */ - static SkMaskFilter* CreateEmboss(SkScalar blurSigma, const SkScalar direction[3], - SkScalar ambient, SkScalar specular); + static sk_sp MakeEmboss(SkScalar blurSigma, const SkScalar direction[3], + SkScalar ambient, SkScalar specular); +#ifdef SK_SUPPORT_LEGACY_MASKFILTER_PTR + static SkMaskFilter* Create(SkBlurStyle style, SkScalar sigma, uint32_t flags = kNone_BlurFlag){ + return Make(style, sigma, flags).release(); + } + static SkMaskFilter* CreateEmboss(SkScalar blurSigma, const SkScalar direction[3], + SkScalar ambient, SkScalar specular) { + return MakeEmboss(blurSigma, direction, ambient, specular).release(); + } SK_ATTR_DEPRECATED("use sigma version") static SkMaskFilter* CreateEmboss(const SkScalar direction[3], SkScalar ambient, SkScalar specular, SkScalar blurRadius); +#endif SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() diff --git a/gfx/skia/skia/include/effects/SkColorCubeFilter.h b/gfx/skia/skia/include/effects/SkColorCubeFilter.h index a8a5e329de..8b621292b0 100644 --- a/gfx/skia/skia/include/effects/SkColorCubeFilter.h +++ b/gfx/skia/skia/include/effects/SkColorCubeFilter.h @@ -20,7 +20,11 @@ public: * This cube contains a transform where (x,y,z) maps to the (r,g,b). * The alpha components of the colors must be 0xFF. */ + static sk_sp Make(sk_sp cubeData, int cubeDimension); + +#ifdef SK_SUPPORT_LEGACY_COLORFILTER_PTR static SkColorFilter* Create(SkData* cubeData, int cubeDimension); +#endif void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; uint32_t getFlags() const override; @@ -33,7 +37,7 @@ public: SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorCubeFilter) protected: - SkColorCubeFilter(SkData* cubeData, int cubeDimension); + SkColorCubeFilter(sk_sp cubeData, int cubeDimension); void flatten(SkWriteBuffer&) const override; private: @@ -67,7 +71,7 @@ private: static void initProcessingLuts(ColorCubeProcesingCache* cache); }; - SkAutoDataUnref fCubeData; + sk_sp fCubeData; int32_t fUniqueID; mutable ColorCubeProcesingCache fCache; diff --git a/gfx/skia/skia/include/effects/SkColorFilterImageFilter.h b/gfx/skia/skia/include/effects/SkColorFilterImageFilter.h index db5d842c2f..2aa04d1644 100644 --- a/gfx/skia/skia/include/effects/SkColorFilterImageFilter.h +++ b/gfx/skia/skia/include/effects/SkColorFilterImageFilter.h @@ -14,26 +14,36 @@ class SkColorFilter; class SK_API SkColorFilterImageFilter : public SkImageFilter { public: - static SkImageFilter* Create(SkColorFilter* cf, SkImageFilter* input = NULL, - const CropRect* cropRect = NULL); + static sk_sp Make(sk_sp cf, + sk_sp input, + const CropRect* cropRect = NULL); SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(SkColorFilter* cf, + SkImageFilter* input = NULL, + const CropRect* cropRect = NULL) { + return Make(sk_ref_sp(cf), + sk_ref_sp(input), + cropRect).release(); + } +#endif + protected: void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, - SkIPoint* loc) const override; + bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, + SkIPoint* loc) const override; bool onIsColorFilterNode(SkColorFilter**) const override; - bool canComputeFastBounds() const override; + bool affectsTransparentBlack() const override; private: - SkColorFilterImageFilter(SkColorFilter* cf, - SkImageFilter* input, + SkColorFilterImageFilter(sk_sp cf, + sk_sp input, const CropRect* cropRect); - virtual ~SkColorFilterImageFilter(); - SkColorFilter* fColorFilter; + sk_sp fColorFilter; typedef SkImageFilter INHERITED; }; diff --git a/gfx/skia/skia/include/effects/SkColorMatrixFilter.h b/gfx/skia/skia/include/effects/SkColorMatrixFilter.h index 7ffbf117cb..6e74bee315 100644 --- a/gfx/skia/skia/include/effects/SkColorMatrixFilter.h +++ b/gfx/skia/skia/include/effects/SkColorMatrixFilter.h @@ -13,47 +13,25 @@ class SK_API SkColorMatrixFilter : public SkColorFilter { public: - static SkColorFilter* Create(const SkColorMatrix& cm) { - return new SkColorMatrixFilter(cm); - } - static SkColorFilter* Create(const SkScalar array[20]) { - return new SkColorMatrixFilter(array); - } - /** * Create a colorfilter that multiplies the RGB channels by one color, and * then adds a second color, pinning the result for each component to * [0..255]. The alpha components of the mul and add arguments * are ignored. */ - static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add); + static sk_sp MakeLightingFilter(SkColor mul, SkColor add); - void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; - uint32_t getFlags() const override; - bool asColorMatrix(SkScalar matrix[20]) const override; - SkColorFilter* newComposed(const SkColorFilter*) const override; - -#if SK_SUPPORT_GPU - const GrFragmentProcessor* asFragmentProcessor(GrContext*) const override; +#ifdef SK_SUPPORT_LEGACY_COLORFILTER_PTR + static SkColorFilter* Create(const SkColorMatrix& cm) { + return SkColorFilter::MakeMatrixFilterRowMajor255(cm.fMat).release(); + } + static SkColorFilter* Create(const SkScalar array[20]) { + return SkColorFilter::MakeMatrixFilterRowMajor255(array).release(); + } + static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add) { + return MakeLightingFilter(mul, add).release(); + } #endif - - SK_TO_STRING_OVERRIDE() - - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorMatrixFilter) - -protected: - explicit SkColorMatrixFilter(const SkColorMatrix&); - explicit SkColorMatrixFilter(const SkScalar array[20]); - void flatten(SkWriteBuffer&) const override; - -private: - SkColorMatrix fMatrix; - float fTranspose[SkColorMatrix::kCount]; // for Sk4s - uint32_t fFlags; - - void initState(const SkScalar array[20]); - - typedef SkColorFilter INHERITED; }; #endif diff --git a/gfx/skia/skia/include/effects/SkComposeImageFilter.h b/gfx/skia/skia/include/effects/SkComposeImageFilter.h index 4946fa13d0..49c7685415 100644 --- a/gfx/skia/skia/include/effects/SkComposeImageFilter.h +++ b/gfx/skia/skia/include/effects/SkComposeImageFilter.h @@ -12,31 +12,36 @@ class SK_API SkComposeImageFilter : public SkImageFilter { public: - virtual ~SkComposeImageFilter(); - - static SkImageFilter* Create(SkImageFilter* outer, SkImageFilter* inner) { - if (NULL == outer) { - return SkSafeRef(inner); + static sk_sp Make(sk_sp outer, sk_sp inner) { + if (!outer) { + return inner; } - if (NULL == inner) { - return SkRef(outer); + if (!inner) { + return outer; } - SkImageFilter* inputs[2] = { outer, inner }; - return new SkComposeImageFilter(inputs); + sk_sp inputs[2] = { std::move(outer), std::move(inner) }; + return sk_sp(new SkComposeImageFilter(inputs)); } - void computeFastBounds(const SkRect& src, SkRect* dst) const override; + SkRect computeFastBounds(const SkRect& src) const override; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeImageFilter) -protected: - explicit SkComposeImageFilter(SkImageFilter* inputs[2]) : INHERITED(2, inputs) { - SkASSERT(inputs[0]); - SkASSERT(inputs[1]); +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(SkImageFilter* outer, SkImageFilter* inner) { + return Make(sk_ref_sp(outer), + sk_ref_sp(inner)).release(); } - virtual bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* loc) const override; - bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) const override; +#endif + +protected: + explicit SkComposeImageFilter(sk_sp inputs[2]) : INHERITED(inputs, 2, nullptr) { + SkASSERT(inputs[0].get()); + SkASSERT(inputs[1].get()); + } + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; + SkIRect onFilterBounds(const SkIRect&, const SkMatrix&, MapDirection) const override; private: typedef SkImageFilter INHERITED; diff --git a/gfx/skia/skia/include/effects/SkCornerPathEffect.h b/gfx/skia/skia/include/effects/SkCornerPathEffect.h index 13095f0e6c..cf03463530 100644 --- a/gfx/skia/skia/include/effects/SkCornerPathEffect.h +++ b/gfx/skia/skia/include/effects/SkCornerPathEffect.h @@ -20,7 +20,15 @@ public: /** radius must be > 0 to have an effect. It specifies the distance from each corner that should be "rounded". */ - static SkPathEffect* Create(SkScalar radius) { return new SkCornerPathEffect(radius); } + static sk_sp Make(SkScalar radius) { + return sk_sp(new SkCornerPathEffect(radius)); + } + +#ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR + static SkPathEffect* Create(SkScalar radius) { + return Make(radius).release(); + } +#endif virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override; diff --git a/gfx/skia/skia/include/effects/SkDashPathEffect.h b/gfx/skia/skia/include/effects/SkDashPathEffect.h index 3c1407b725..ccb1a4e440 100644 --- a/gfx/skia/skia/include/effects/SkDashPathEffect.h +++ b/gfx/skia/skia/include/effects/SkDashPathEffect.h @@ -36,10 +36,13 @@ public: Note: only affects stroked paths. */ + static sk_sp Make(const SkScalar intervals[], int count, SkScalar phase); + +#ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR static SkPathEffect* Create(const SkScalar intervals[], int count, SkScalar phase) { - return new SkDashPathEffect(intervals, count, phase); + return Make(intervals, count, phase).release(); } - virtual ~SkDashPathEffect(); +#endif virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override; @@ -58,6 +61,7 @@ public: #endif protected: + virtual ~SkDashPathEffect(); SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase); void flatten(SkWriteBuffer&) const override; @@ -66,6 +70,7 @@ private: int32_t fCount; SkScalar fPhase; // computed from phase + SkScalar fInitialDashLength; int32_t fInitialDashIndex; SkScalar fIntervalLength; diff --git a/gfx/skia/skia/include/effects/SkDiscretePathEffect.h b/gfx/skia/skia/include/effects/SkDiscretePathEffect.h index a49e2d89a7..78d4516ee6 100644 --- a/gfx/skia/skia/include/effects/SkDiscretePathEffect.h +++ b/gfx/skia/skia/include/effects/SkDiscretePathEffect.h @@ -29,9 +29,13 @@ public: they can pass in a different seedAssist to get a different set of path segments. */ + static sk_sp Make(SkScalar segLength, SkScalar dev, uint32_t seedAssist = 0); + +#ifdef SK_SUPPORT_LEGACY_PATHEFFECT_PTR static SkPathEffect* Create(SkScalar segLength, SkScalar deviation, uint32_t seedAssist = 0) { - return new SkDiscretePathEffect(segLength, deviation, seedAssist); + return Make(segLength, deviation, seedAssist).release(); } +#endif virtual bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*) const override; diff --git a/gfx/skia/skia/include/effects/SkDisplacementMapEffect.h b/gfx/skia/skia/include/effects/SkDisplacementMapEffect.h index e94461795b..a5b81e8585 100644 --- a/gfx/skia/skia/include/effects/SkDisplacementMapEffect.h +++ b/gfx/skia/skia/include/effects/SkDisplacementMapEffect.h @@ -31,20 +31,20 @@ public: SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDisplacementMapEffect) - virtual bool onFilterImage(Proxy* proxy, - const SkBitmap& src, - const Context& ctx, - SkBitmap* dst, - SkIPoint* offset) const override; - void computeFastBounds(const SkRect& src, SkRect* dst) const override; + bool onFilterImageDeprecated(Proxy* proxy, + const SkBitmap& src, + const Context& ctx, + SkBitmap* dst, + SkIPoint* offset) const override; + SkRect computeFastBounds(const SkRect& src) const override; - virtual bool onFilterBounds(const SkIRect& src, const SkMatrix&, - SkIRect* dst) const override; - void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override; + virtual SkIRect onFilterBounds(const SkIRect& src, const SkMatrix&, + MapDirection) const override; + SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override; #if SK_SUPPORT_GPU bool canFilterImageGPU() const override { return true; } - virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, + bool filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const override; #endif diff --git a/gfx/skia/skia/include/effects/SkDropShadowImageFilter.h b/gfx/skia/skia/include/effects/SkDropShadowImageFilter.h index bff1a42013..a4726f3247 100644 --- a/gfx/skia/skia/include/effects/SkDropShadowImageFilter.h +++ b/gfx/skia/skia/include/effects/SkDropShadowImageFilter.h @@ -5,6 +5,9 @@ * found in the LICENSE file. */ +#ifndef SkDropShadowImageFilter_DEFINED +#define SkDropShadowImageFilter_DEFINED + #include "SkColor.h" #include "SkImageFilter.h" #include "SkScalar.h" @@ -20,27 +23,40 @@ public: static const int kShadowModeCount = kLast_ShadowMode+1; - static SkImageFilter* Create(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, - SkColor color, ShadowMode shadowMode, SkImageFilter* input = NULL, - const CropRect* cropRect = NULL) { - return new SkDropShadowImageFilter(dx, dy, sigmaX, sigmaY, color, shadowMode, input, - cropRect); + static sk_sp Make(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, + SkColor color, ShadowMode shadowMode, + sk_sp input, + const CropRect* cropRect = nullptr) { + return sk_sp(new SkDropShadowImageFilter(dx, dy, sigmaX, sigmaY, + color, shadowMode, + std::move(input), + cropRect)); } - void computeFastBounds(const SkRect&, SkRect*) const override; + SkRect computeFastBounds(const SkRect&) const override; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDropShadowImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, + SkColor color, ShadowMode shadowMode, + SkImageFilter* input = nullptr, + const CropRect* cropRect = nullptr) { + return Make(dx, dy, sigmaX, sigmaY, color, shadowMode, + sk_ref_sp(input), cropRect).release(); + } +#endif + protected: void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& source, const Context&, SkBitmap* result, - SkIPoint* loc) const override; - void onFilterNodeBounds(const SkIRect& src, const SkMatrix&, - SkIRect* dst, MapDirection) const override; + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; + SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override; private: SkDropShadowImageFilter(SkScalar dx, SkScalar dy, SkScalar sigmaX, SkScalar sigmaY, SkColor, - ShadowMode shadowMode, SkImageFilter* input, const CropRect* cropRect); + ShadowMode shadowMode, sk_sp input, + const CropRect* cropRect); SkScalar fDx, fDy, fSigmaX, fSigmaY; SkColor fColor; @@ -48,3 +64,5 @@ private: typedef SkImageFilter INHERITED; }; + +#endif diff --git a/gfx/skia/skia/include/effects/SkEmbossMaskFilter.h b/gfx/skia/skia/include/effects/SkEmbossMaskFilter.h index 72020bf3ce..8a34282451 100644 --- a/gfx/skia/skia/include/effects/SkEmbossMaskFilter.h +++ b/gfx/skia/skia/include/effects/SkEmbossMaskFilter.h @@ -23,7 +23,13 @@ public: uint8_t fSpecular; // exponent, 4.4 right now }; - static SkMaskFilter* Create(SkScalar blurSigma, const Light& light); + static sk_sp Make(SkScalar blurSigma, const Light& light); + +#ifdef SK_SUPPORT_LEGACY_MASKFILTER_PTR + static SkMaskFilter* Create(SkScalar blurSigma, const Light& light) { + return Make(blurSigma, light).release(); + } +#endif // overrides from SkMaskFilter // This method is not exported to java. diff --git a/gfx/skia/skia/include/effects/SkGradientShader.h b/gfx/skia/skia/include/effects/SkGradientShader.h index ae11e2dafd..e474f38a4e 100644 --- a/gfx/skia/skia/include/effects/SkGradientShader.h +++ b/gfx/skia/skia/include/effects/SkGradientShader.h @@ -42,15 +42,14 @@ public: @param count Must be >=2. The number of colors (and pos if not NULL) entries. @param mode The tiling mode */ - static SkShader* CreateLinear(const SkPoint pts[2], - const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode mode, - uint32_t flags, const SkMatrix* localMatrix); - - static SkShader* CreateLinear(const SkPoint pts[2], - const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode mode) { - return CreateLinear(pts, colors, pos, count, mode, 0, NULL); + static sk_sp MakeLinear(const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode, + uint32_t flags, const SkMatrix* localMatrix); + static sk_sp MakeLinear(const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode) { + return MakeLinear(pts, colors, pos, count, mode, 0, NULL); } /** Returns a shader that generates a radial gradient given the center and radius. @@ -69,15 +68,14 @@ public: @param count Must be >= 2. The number of colors (and pos if not NULL) entries @param mode The tiling mode */ - static SkShader* CreateRadial(const SkPoint& center, SkScalar radius, - const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode mode, - uint32_t flags, const SkMatrix* localMatrix); - - static SkShader* CreateRadial(const SkPoint& center, SkScalar radius, - const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode mode) { - return CreateRadial(center, radius, colors, pos, count, mode, 0, NULL); + static sk_sp MakeRadial(const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode, + uint32_t flags, const SkMatrix* localMatrix); + static sk_sp MakeRadial(const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode) { + return MakeRadial(center, radius, colors, pos, count, mode, 0, NULL); } /** @@ -86,18 +84,17 @@ public: * two circles according to the following HTML spec. * http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient */ - static SkShader* CreateTwoPointConical(const SkPoint& start, SkScalar startRadius, - const SkPoint& end, SkScalar endRadius, - const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode mode, - uint32_t flags, const SkMatrix* localMatrix); - - static SkShader* CreateTwoPointConical(const SkPoint& start, SkScalar startRadius, - const SkPoint& end, SkScalar endRadius, - const SkColor colors[], const SkScalar pos[], int count, - SkShader::TileMode mode) { - return CreateTwoPointConical(start, startRadius, end, endRadius, colors, pos, count, mode, - 0, NULL); + static sk_sp MakeTwoPointConical(const SkPoint& start, SkScalar startRadius, + const SkPoint& end, SkScalar endRadius, + const SkColor colors[], const SkScalar pos[], + int count, SkShader::TileMode mode, + uint32_t flags, const SkMatrix* localMatrix); + static sk_sp MakeTwoPointConical(const SkPoint& start, SkScalar startRadius, + const SkPoint& end, SkScalar endRadius, + const SkColor colors[], const SkScalar pos[], + int count, SkShader::TileMode mode) { + return MakeTwoPointConical(start, startRadius, end, endRadius, colors, pos, count, mode, + 0, NULL); } /** Returns a shader that generates a sweep gradient given a center. @@ -115,14 +112,67 @@ public: intermediate values must be strictly increasing. @param count Must be >= 2. The number of colors (and pos if not NULL) entries */ + static sk_sp MakeSweep(SkScalar cx, SkScalar cy, + const SkColor colors[], const SkScalar pos[], int count, + uint32_t flags, const SkMatrix* localMatrix); + static sk_sp MakeSweep(SkScalar cx, SkScalar cy, + const SkColor colors[], const SkScalar pos[], int count) { + return MakeSweep(cx, cy, colors, pos, count, 0, NULL); + } + +#ifdef SK_SUPPORT_LEGACY_CREATESHADER_PTR + static SkShader* CreateLinear(const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode, + uint32_t flags, const SkMatrix* localMatrix) { + return MakeLinear(pts, colors, pos, count, mode, flags, localMatrix).release(); + } + static SkShader* CreateLinear(const SkPoint pts[2], + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode) { + return CreateLinear(pts, colors, pos, count, mode, 0, NULL); + } + + static SkShader* CreateRadial(const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode, + uint32_t flags, const SkMatrix* localMatrix) { + return MakeRadial(center, radius, colors, pos, count, mode, flags, localMatrix).release(); + } + + static SkShader* CreateRadial(const SkPoint& center, SkScalar radius, + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode) { + return CreateRadial(center, radius, colors, pos, count, mode, 0, NULL); + } + + static SkShader* CreateTwoPointConical(const SkPoint& start, SkScalar startRadius, + const SkPoint& end, SkScalar endRadius, + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode, + uint32_t flags, const SkMatrix* localMatrix) { + return MakeTwoPointConical(start, startRadius, end, endRadius, colors, pos, count, mode, + flags, localMatrix).release(); + } + static SkShader* CreateTwoPointConical(const SkPoint& start, SkScalar startRadius, + const SkPoint& end, SkScalar endRadius, + const SkColor colors[], const SkScalar pos[], int count, + SkShader::TileMode mode) { + return CreateTwoPointConical(start, startRadius, end, endRadius, colors, pos, count, mode, + 0, NULL); + } + static SkShader* CreateSweep(SkScalar cx, SkScalar cy, const SkColor colors[], const SkScalar pos[], int count, - uint32_t flags, const SkMatrix* localMatrix); - + uint32_t flags, const SkMatrix* localMatrix) { + return MakeSweep(cx, cy, colors, pos, count, flags, localMatrix).release(); + } static SkShader* CreateSweep(SkScalar cx, SkScalar cy, const SkColor colors[], const SkScalar pos[], int count) { return CreateSweep(cx, cy, colors, pos, count, 0, NULL); } +#endif + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() }; diff --git a/gfx/skia/skia/include/effects/SkImageSource.h b/gfx/skia/skia/include/effects/SkImageSource.h index 56c6a6e96a..0b4fecd776 100644 --- a/gfx/skia/skia/include/effects/SkImageSource.h +++ b/gfx/skia/skia/include/effects/SkImageSource.h @@ -8,39 +8,64 @@ #ifndef SkImageSource_DEFINED #define SkImageSource_DEFINED +#include "SkImage.h" #include "SkImageFilter.h" -class SkImage; - class SK_API SkImageSource : public SkImageFilter { public: - static SkImageFilter* Create(const SkImage*); - static SkImageFilter* Create(const SkImage*, - const SkRect& srcRect, - const SkRect& dstRect, - SkFilterQuality); + static sk_sp Make(sk_sp image) { + if (!image) { + return nullptr; + } - void computeFastBounds(const SkRect& src, SkRect* dst) const override; + return sk_sp(new SkImageSource(std::move(image))); + } + static sk_sp Make(sk_sp image, + const SkRect& srcRect, + const SkRect& dstRect, + SkFilterQuality filterQuality) { + if (!image) { + return nullptr; + } + + return sk_sp(new SkImageSource(std::move(image), + srcRect, dstRect, + filterQuality)); + } + + SkRect computeFastBounds(const SkRect& src) const override; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkImageSource) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(SkImage* image) { + return Make(sk_ref_sp(image)).release(); + } + static SkImageFilter* Create(SkImage* image, + const SkRect& srcRect, + const SkRect& dstRect, + SkFilterQuality filterQuality) { + return Make(sk_ref_sp(image), srcRect, dstRect, filterQuality).release(); + } +#endif + protected: void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const override; + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; private: - explicit SkImageSource(const SkImage*); - SkImageSource(const SkImage*, + explicit SkImageSource(sk_sp); + SkImageSource(sk_sp, const SkRect& srcRect, const SkRect& dstRect, SkFilterQuality); - SkAutoTUnref fImage; - SkRect fSrcRect, fDstRect; - SkFilterQuality fFilterQuality; + sk_sp fImage; + SkRect fSrcRect, fDstRect; + SkFilterQuality fFilterQuality; typedef SkImageFilter INHERITED; }; diff --git a/gfx/skia/skia/include/effects/SkLayerDrawLooper.h b/gfx/skia/skia/include/effects/SkLayerDrawLooper.h index 76172dfd4b..186d44a656 100644 --- a/gfx/skia/skia/include/effects/SkLayerDrawLooper.h +++ b/gfx/skia/skia/include/effects/SkLayerDrawLooper.h @@ -80,7 +80,7 @@ public: SK_TO_STRING_OVERRIDE() Factory getFactory() const override { return CreateProc; } - static SkFlattenable* CreateProc(SkReadBuffer& buffer); + static sk_sp CreateProc(SkReadBuffer& buffer); protected: SkLayerDrawLooper(); @@ -142,7 +142,12 @@ public: * Pass list of layers on to newly built looper and return it. This will * also reset the builder, so it can be used to build another looper. */ - SkLayerDrawLooper* detachLooper(); + sk_sp detach(); +#ifdef SK_SUPPORT_LEGACY_MINOR_EFFECT_PTR + SkLayerDrawLooper* detachLooper() { + return (SkLayerDrawLooper*)this->detach().release(); + } +#endif private: Rec* fRecs; diff --git a/gfx/skia/skia/include/effects/SkLayerRasterizer.h b/gfx/skia/skia/include/effects/SkLayerRasterizer.h index c6ac1de363..9ddcd4e6c3 100644 --- a/gfx/skia/skia/include/effects/SkLayerRasterizer.h +++ b/gfx/skia/skia/include/effects/SkLayerRasterizer.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,7 +5,6 @@ * found in the LICENSE file. */ - #ifndef SkLayerRasterizer_DEFINED #define SkLayerRasterizer_DEFINED @@ -44,7 +42,7 @@ public: * * The caller is responsible for calling unref() on the returned object, if non NULL. */ - SkLayerRasterizer* detachRasterizer(); + sk_sp detach(); /** * Create and return a new immutable SkLayerRasterizer that contains a shapshot of the @@ -58,7 +56,16 @@ public: * * The caller is responsible for calling unref() on the returned object, if non NULL. */ - SkLayerRasterizer* snapshotRasterizer() const; + sk_sp snapshot() const; + +#ifdef SK_SUPPORT_LEGACY_MINOR_EFFECT_PTR + SkLayerRasterizer* detachRasterizer() { + return this->detach().release(); + } + SkLayerRasterizer* snapshotRasterizer() const { + return this->snapshot().release(); + } +#endif private: SkDeque* fLayers; diff --git a/gfx/skia/skia/include/effects/SkLerpXfermode.h b/gfx/skia/skia/include/effects/SkLerpXfermode.h deleted file mode 100644 index 8ba42302bf..0000000000 --- a/gfx/skia/skia/include/effects/SkLerpXfermode.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkLerpXfermode_DEFINED -#define SkLerpXfermode_DEFINED - -#include "SkXfermode.h" - -class SK_API SkLerpXfermode : public SkXfermode { -public: - /** - * result = scale * src + (1 - scale) * dst - * - * When scale == 1, this is the same as kSrc_Mode - * When scale == 0, this is the same as kDst_Mode - */ - static SkXfermode* Create(SkScalar scale); - - // overrides from SkXfermode - virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count, - const SkAlpha aa[]) const override; - virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count, - const SkAlpha aa[]) const override; - virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count, - const SkAlpha aa[]) const override; - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLerpXfermode) - -protected: - void flatten(SkWriteBuffer&) const override; - -private: - SkLerpXfermode(unsigned scale256); - - unsigned fScale256; // 0..256 - - typedef SkXfermode INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/include/effects/SkLightingImageFilter.h b/gfx/skia/skia/include/effects/SkLightingImageFilter.h index 33cfceccb2..fb356c52e4 100644 --- a/gfx/skia/skia/include/effects/SkLightingImageFilter.h +++ b/gfx/skia/skia/include/effects/SkLightingImageFilter.h @@ -49,7 +49,7 @@ protected: void flatten(SkWriteBuffer&) const override; const SkImageFilterLight* light() const { return fLight.get(); } SkScalar surfaceScale() const { return fSurfaceScale; } - bool canComputeFastBounds() const override { return false; } + bool affectsTransparentBlack() const override { return true; } private: typedef SkImageFilter INHERITED; diff --git a/gfx/skia/skia/include/effects/SkLumaColorFilter.h b/gfx/skia/skia/include/effects/SkLumaColorFilter.h index 8dd519af4a..3a68607b19 100644 --- a/gfx/skia/skia/include/effects/SkLumaColorFilter.h +++ b/gfx/skia/skia/include/effects/SkLumaColorFilter.h @@ -23,7 +23,11 @@ */ class SK_API SkLumaColorFilter : public SkColorFilter { public: - static SkColorFilter* Create(); + static sk_sp Make(); + +#ifdef SK_SUPPORT_LEGACY_COLORFILTER_PTR + static SkColorFilter* Create() { return Make().release(); } +#endif void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; diff --git a/gfx/skia/skia/include/effects/SkMagnifierImageFilter.h b/gfx/skia/skia/include/effects/SkMagnifierImageFilter.h index af142e9eff..739f1eec73 100644 --- a/gfx/skia/skia/include/effects/SkMagnifierImageFilter.h +++ b/gfx/skia/skia/include/effects/SkMagnifierImageFilter.h @@ -14,17 +14,24 @@ class SK_API SkMagnifierImageFilter : public SkImageFilter { public: - static SkImageFilter* Create(const SkRect& src, SkScalar inset, SkImageFilter* input = NULL); + static sk_sp Make(const SkRect& src, SkScalar inset, sk_sp input); SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMagnifierImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(const SkRect& src, SkScalar inset, + SkImageFilter* input = nullptr) { + return Make(src, inset, sk_ref_sp(input)).release(); + } +#endif + protected: - SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset, SkImageFilter* input); + SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset, sk_sp input); void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const override; + bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* offset) const override; #if SK_SUPPORT_GPU bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, const SkIRect& bounds) const override; diff --git a/gfx/skia/skia/include/effects/SkMatrixConvolutionImageFilter.h b/gfx/skia/skia/include/effects/SkMatrixConvolutionImageFilter.h index 7a2026c86b..092af08f0c 100644 --- a/gfx/skia/skia/include/effects/SkMatrixConvolutionImageFilter.h +++ b/gfx/skia/skia/include/effects/SkMatrixConvolutionImageFilter.h @@ -77,10 +77,10 @@ protected: const CropRect* cropRect); void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* loc) const override; - void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override; - bool canComputeFastBounds() const override; + bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, + SkBitmap* result, SkIPoint* loc) const override; + SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override; + bool affectsTransparentBlack() const override; #if SK_SUPPORT_GPU bool asFragmentProcessor(GrFragmentProcessor**, GrTexture*, const SkMatrix&, diff --git a/gfx/skia/skia/include/effects/SkMergeImageFilter.h b/gfx/skia/skia/include/effects/SkMergeImageFilter.h index 10c29131ed..e85cc1f97f 100644 --- a/gfx/skia/skia/include/effects/SkMergeImageFilter.h +++ b/gfx/skia/skia/include/effects/SkMergeImageFilter.h @@ -14,32 +14,53 @@ class SK_API SkMergeImageFilter : public SkImageFilter { public: - virtual ~SkMergeImageFilter(); + ~SkMergeImageFilter() override; - static SkImageFilter* Create(SkImageFilter* first, SkImageFilter* second, - SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode, - const CropRect* cropRect = NULL) { - SkImageFilter* inputs[2] = { first, second }; + static sk_sp Make(sk_sp first, sk_sp second, + SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode, + const CropRect* cropRect = nullptr) { + sk_sp inputs[2] = { first, second }; SkXfermode::Mode modes[2] = { mode, mode }; - return new SkMergeImageFilter(inputs, 2, modes, cropRect); + return sk_sp(new SkMergeImageFilter(inputs, 2, modes, cropRect)); } - static SkImageFilter* Create(SkImageFilter* filters[], int count, - const SkXfermode::Mode modes[] = NULL, - const CropRect* cropRect = NULL) { - return new SkMergeImageFilter(filters, count, modes, cropRect); + static sk_sp Make(sk_sp filters[], + int count, + const SkXfermode::Mode modes[] = nullptr, + const CropRect* cropRect = nullptr) { + return sk_sp(new SkMergeImageFilter(filters, count, modes, cropRect)); } SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMergeImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(SkImageFilter* first, SkImageFilter* second, + SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode, + const CropRect* cropRect = nullptr) { + return Make(sk_ref_sp(first), + sk_ref_sp(second), + mode, cropRect).release(); + } + + static SkImageFilter* Create(SkImageFilter* filters[], int count, + const SkXfermode::Mode modes[] = nullptr, + const CropRect* cropRect = nullptr) { + SkAutoTDeleteArray> temp(new sk_sp[count]); + for (int i = 0; i < count; ++i) { + temp[i] = sk_ref_sp(filters[i]); + } + return Make(temp.get(), count, modes, cropRect).release(); + } +#endif + protected: void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, - SkIPoint* loc) const override; + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; private: - SkMergeImageFilter(SkImageFilter* filters[], int count, const SkXfermode::Mode modes[], + SkMergeImageFilter(sk_sp filters[], int count, const SkXfermode::Mode modes[], const CropRect* cropRect); uint8_t* fModes; // SkXfermode::Mode diff --git a/gfx/skia/skia/include/effects/SkMorphologyImageFilter.h b/gfx/skia/skia/include/effects/SkMorphologyImageFilter.h index 422bc01943..27608ecd07 100644 --- a/gfx/skia/skia/include/effects/SkMorphologyImageFilter.h +++ b/gfx/skia/skia/include/effects/SkMorphologyImageFilter.h @@ -5,7 +5,6 @@ * found in the LICENSE file. */ - #ifndef SkMorphologyImageFilter_DEFINED #define SkMorphologyImageFilter_DEFINED @@ -13,11 +12,11 @@ #include "SkImageFilter.h" #include "SkSize.h" +/////////////////////////////////////////////////////////////////////////////// class SK_API SkMorphologyImageFilter : public SkImageFilter { public: - void computeFastBounds(const SkRect& src, SkRect* dst) const override; - void onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst, MapDirection) const override; + SkRect computeFastBounds(const SkRect& src) const override; + SkIRect onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override; /** * All morphology procs have the same signature: src is the source buffer, dst the @@ -30,78 +29,101 @@ public: int width, int height, int srcStride, int dstStride); protected: - SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input, - const CropRect* cropRect); - bool filterImageGeneric(Proc procX, Proc procY, - Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const; - void flatten(SkWriteBuffer&) const override; -#if SK_SUPPORT_GPU - bool canFilterImageGPU() const override { return true; } - bool filterImageGPUGeneric(bool dilate, Proxy* proxy, const SkBitmap& src, - const Context& ctm, SkBitmap* result, - SkIPoint* offset) const; -#endif + enum Op { + kErode_Op, + kDilate_Op, + }; - SkISize radius() const { return fRadius; } + virtual Op op() const = 0; + + SkMorphologyImageFilter(int radiusX, int radiusY, + sk_sp input, + const CropRect* cropRect); + sk_sp onFilterImage(SkSpecialImage* source, + const Context&, + SkIPoint* offset) const override; + void flatten(SkWriteBuffer&) const override; + + SkISize radius() const { return fRadius; } private: - SkISize fRadius; + SkISize fRadius; + typedef SkImageFilter INHERITED; }; +/////////////////////////////////////////////////////////////////////////////// class SK_API SkDilateImageFilter : public SkMorphologyImageFilter { public: - static SkImageFilter* Create(int radiusX, int radiusY, SkImageFilter* input = NULL, - const CropRect* cropRect = NULL) { + static sk_sp Make(int radiusX, int radiusY, + sk_sp input, + const CropRect* cropRect = nullptr) { if (radiusX < 0 || radiusY < 0) { - return NULL; + return nullptr; } - return new SkDilateImageFilter(radiusX, radiusY, input, cropRect); + return sk_sp(new SkDilateImageFilter(radiusX, radiusY, + std::move(input), + cropRect)); } - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const override; - -#if SK_SUPPORT_GPU - bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const override; -#endif - SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDilateImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(int radiusX, int radiusY, + SkImageFilter* input = nullptr, + const CropRect* cropRect = nullptr) { + return Make(radiusX, radiusY, + sk_ref_sp(input), + cropRect).release(); + } +#endif + +protected: + Op op() const override { return kDilate_Op; } + private: - SkDilateImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect) + SkDilateImageFilter(int radiusX, int radiusY, + sk_sp input, + const CropRect* cropRect) : INHERITED(radiusX, radiusY, input, cropRect) {} typedef SkMorphologyImageFilter INHERITED; }; +/////////////////////////////////////////////////////////////////////////////// class SK_API SkErodeImageFilter : public SkMorphologyImageFilter { public: - static SkImageFilter* Create(int radiusX, int radiusY, - SkImageFilter* input = NULL, - const CropRect* cropRect = NULL) { + static sk_sp Make(int radiusX, int radiusY, + sk_sp input, + const CropRect* cropRect = nullptr) { if (radiusX < 0 || radiusY < 0) { - return NULL; + return nullptr; } - return new SkErodeImageFilter(radiusX, radiusY, input, cropRect); + return sk_sp(new SkErodeImageFilter(radiusX, radiusY, + std::move(input), + cropRect)); } - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const override; - -#if SK_SUPPORT_GPU - bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context&, - SkBitmap* result, SkIPoint* offset) const override; -#endif - SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkErodeImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(int radiusX, int radiusY, + SkImageFilter* input = nullptr, + const CropRect* cropRect = nullptr) { + return Make(radiusX, radiusY, + sk_ref_sp(input), + cropRect).release(); + } +#endif + +protected: + Op op() const override { return kErode_Op; } + private: - SkErodeImageFilter(int radiusX, int radiusY, SkImageFilter* input, const CropRect* cropRect) + SkErodeImageFilter(int radiusX, int radiusY, + sk_sp input, const CropRect* cropRect) : INHERITED(radiusX, radiusY, input, cropRect) {} typedef SkMorphologyImageFilter INHERITED; diff --git a/gfx/skia/skia/include/effects/SkOffsetImageFilter.h b/gfx/skia/skia/include/effects/SkOffsetImageFilter.h index 66b5515b37..cf2ff96ada 100644 --- a/gfx/skia/skia/include/effects/SkOffsetImageFilter.h +++ b/gfx/skia/skia/include/effects/SkOffsetImageFilter.h @@ -13,27 +13,36 @@ class SK_API SkOffsetImageFilter : public SkImageFilter { public: - static SkImageFilter* Create(SkScalar dx, SkScalar dy, SkImageFilter* input = NULL, - const CropRect* cropRect = NULL) { + static sk_sp Make(SkScalar dx, SkScalar dy, + sk_sp input, + const CropRect* cropRect = nullptr) { if (!SkScalarIsFinite(dx) || !SkScalarIsFinite(dy)) { - return NULL; + return nullptr; } - return new SkOffsetImageFilter(dx, dy, input, cropRect); + + return sk_sp(new SkOffsetImageFilter(dx, dy, std::move(input), cropRect)); } - void computeFastBounds(const SkRect& src, SkRect* dst) const override; + SkRect computeFastBounds(const SkRect& src) const override; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkOffsetImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(SkScalar dx, SkScalar dy, SkImageFilter* input = nullptr, + const CropRect* cropRect = nullptr) { + return Make(dx, dy, sk_ref_sp(input), cropRect).release(); + } +#endif + protected: void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, - SkIPoint* loc) const override; - void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override; + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; + SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override; private: - SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input, const CropRect*); + SkOffsetImageFilter(SkScalar dx, SkScalar dy, sk_sp input, const CropRect*); SkVector fOffset; diff --git a/gfx/skia/skia/include/effects/SkPaintImageFilter.h b/gfx/skia/skia/include/effects/SkPaintImageFilter.h index eee3630f31..35a76b80fb 100644 --- a/gfx/skia/skia/include/effects/SkPaintImageFilter.h +++ b/gfx/skia/skia/include/effects/SkPaintImageFilter.h @@ -22,17 +22,25 @@ public: * not specified, the source primitive's bounds are used * instead. */ - static SkImageFilter* Create(const SkPaint& paint, const CropRect* rect = NULL); + static sk_sp Make(const SkPaint& paint, const CropRect* cropRect = nullptr) { + return sk_sp(new SkPaintImageFilter(paint, cropRect)); + } - bool canComputeFastBounds() const override; + bool affectsTransparentBlack() const override; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPaintImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(const SkPaint& paint, const CropRect* rect = nullptr) { + return Make(paint, rect).release(); + } +#endif + protected: void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, - SkIPoint* loc) const override; + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; private: SkPaintImageFilter(const SkPaint& paint, const CropRect* rect); diff --git a/gfx/skia/skia/include/effects/SkPerlinNoiseShader.h b/gfx/skia/skia/include/effects/SkPerlinNoiseShader.h index ec7c08c8ea..d652e5dcad 100644 --- a/gfx/skia/skia/include/effects/SkPerlinNoiseShader.h +++ b/gfx/skia/skia/include/effects/SkPerlinNoiseShader.h @@ -55,24 +55,30 @@ public: * the frequencies so that the noise will be tileable for the given tile size. If tileSize * is NULL or an empty size, the frequencies will be used as is without modification. */ + static sk_sp MakeFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY, + int numOctaves, SkScalar seed, + const SkISize* tileSize = nullptr); + static sk_sp MakeTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, + int numOctaves, SkScalar seed, + const SkISize* tileSize = nullptr); + +#ifdef SK_SUPPORT_LEGACY_CREATESHADER_PTR static SkShader* CreateFractalNoise(SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, - const SkISize* tileSize = NULL); + const SkISize* tileSize = NULL) { + return MakeFractalNoise(baseFrequencyX, baseFrequencyY, numOctaves, seed, tileSize).release(); + } static SkShader* CreateTurbulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, - int numOctaves, SkScalar seed, - const SkISize* tileSize = NULL); - /** - * Create alias for CreateTurbulunce until all Skia users changed - * its code to use the new naming - */ + int numOctaves, SkScalar seed, + const SkISize* tileSize = NULL) { + return MakeTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed, tileSize).release(); + } static SkShader* CreateTubulence(SkScalar baseFrequencyX, SkScalar baseFrequencyY, int numOctaves, SkScalar seed, const SkISize* tileSize = NULL) { - return CreateTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed, tileSize); + return CreateTurbulence(baseFrequencyX, baseFrequencyY, numOctaves, seed, tileSize); } - - - size_t contextSize() const override; +#endif class PerlinNoiseShaderContext : public SkShader::Context { public: @@ -106,6 +112,7 @@ public: protected: void flatten(SkWriteBuffer&) const override; Context* onCreateContext(const ContextRec&, void* storage) const override; + size_t onContextSize(const ContextRec&) const override; private: SkPerlinNoiseShader(SkPerlinNoiseShader::Type type, SkScalar baseFrequencyX, diff --git a/gfx/skia/skia/include/effects/SkPictureImageFilter.h b/gfx/skia/skia/include/effects/SkPictureImageFilter.h index 934d1324c0..59097ddf59 100644 --- a/gfx/skia/skia/include/effects/SkPictureImageFilter.h +++ b/gfx/skia/skia/include/effects/SkPictureImageFilter.h @@ -16,17 +16,19 @@ public: /** * Refs the passed-in picture. */ - static SkImageFilter* Create(const SkPicture* picture) { - return new SkPictureImageFilter(picture); + static sk_sp Make(sk_sp picture) { + return sk_sp(new SkPictureImageFilter(std::move(picture))); } /** * Refs the passed-in picture. cropRect can be used to crop or expand the destination rect when * the picture is drawn. (No scaling is implied by the dest rect; only the CTM is applied.) */ - static SkImageFilter* Create(const SkPicture* picture, const SkRect& cropRect) { - return new SkPictureImageFilter(picture, cropRect, kDeviceSpace_PictureResolution, - kLow_SkFilterQuality); + static sk_sp Make(sk_sp picture, const SkRect& cropRect) { + return sk_sp(new SkPictureImageFilter(std::move(picture), + cropRect, + kDeviceSpace_PictureResolution, + kLow_SkFilterQuality)); } /** @@ -36,13 +38,31 @@ public: * expand the destination rect when the picture is drawn. (No scaling is implied by the * dest rect; only the CTM is applied.) */ - static SkImageFilter* CreateForLocalSpace(const SkPicture* picture, - const SkRect& cropRect, - SkFilterQuality filterQuality) { - return new SkPictureImageFilter(picture, cropRect, kLocalSpace_PictureResolution, - filterQuality); + static sk_sp MakeForLocalSpace(sk_sp picture, + const SkRect& cropRect, + SkFilterQuality filterQuality) { + return sk_sp(new SkPictureImageFilter(std::move(picture), + cropRect, + kLocalSpace_PictureResolution, + filterQuality)); } +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(const SkPicture* picture) { + return Make(sk_ref_sp(const_cast(picture))).release(); + } + static SkImageFilter* Create(const SkPicture* picture, const SkRect& cropRect) { + return Make(sk_ref_sp(const_cast(picture)), cropRect).release(); + } + static SkImageFilter* CreateForLocalSpace(const SkPicture* picture, + const SkRect& cropRect, + SkFilterQuality filterQuality) { + return MakeForLocalSpace(sk_ref_sp(const_cast(picture)), + cropRect, + filterQuality).release(); + } +#endif + SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPictureImageFilter) @@ -52,8 +72,6 @@ protected: kLocalSpace_PictureResolution }; - virtual ~SkPictureImageFilter(); - /* Constructs an SkPictureImageFilter object from an SkReadBuffer. * Note: If the SkPictureImageFilter object construction requires bitmap * decoding, the decoder must be set on the SkReadBuffer parameter by calling @@ -61,12 +79,12 @@ protected: * @param SkReadBuffer Serialized picture data. */ void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, - SkIPoint* offset) const override; + bool onFilterImageDeprecated(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, + SkIPoint* offset) const override; private: - explicit SkPictureImageFilter(const SkPicture* picture); - SkPictureImageFilter(const SkPicture* picture, const SkRect& cropRect, + explicit SkPictureImageFilter(sk_sp picture); + SkPictureImageFilter(sk_sp picture, const SkRect& cropRect, PictureResolution, SkFilterQuality); void drawPictureAtDeviceResolution(SkBaseDevice*, const SkIRect& deviceBounds, @@ -74,7 +92,7 @@ private: void drawPictureAtLocalResolution(Proxy*, SkBaseDevice*, const SkIRect& deviceBounds, const Context&) const; - const SkPicture* fPicture; + sk_sp fPicture; SkRect fCropRect; PictureResolution fPictureResolution; SkFilterQuality fFilterQuality; diff --git a/gfx/skia/skia/include/effects/SkPixelXorXfermode.h b/gfx/skia/skia/include/effects/SkPixelXorXfermode.h deleted file mode 100644 index f2fe47156c..0000000000 --- a/gfx/skia/skia/include/effects/SkPixelXorXfermode.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2007 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkPixelXorXfermode_DEFINED -#define SkPixelXorXfermode_DEFINED - -#include "SkXfermode.h" - -/** SkPixelXorXfermode implements a simple pixel xor (op ^ src ^ dst). - This transformation does not follow premultiplied conventions, therefore - this proc *always* returns an opaque color (alpha == 255). Thus it is - not really usefull for operating on blended colors. -*/ -class SK_API SkPixelXorXfermode : public SkXfermode { -public: - static SkXfermode* Create(SkColor opColor) { return new SkPixelXorXfermode(opColor); } - - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPixelXorXfermode) - -protected: - void flatten(SkWriteBuffer&) const override; - SkPMColor xferColor(SkPMColor src, SkPMColor dst) const override; - -private: - explicit SkPixelXorXfermode(SkColor opColor) : fOpColor(opColor) {} - - SkColor fOpColor; - - typedef SkXfermode INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/include/effects/SkTableColorFilter.h b/gfx/skia/skia/include/effects/SkTableColorFilter.h index e4d42dc00c..fe31149464 100644 --- a/gfx/skia/skia/include/effects/SkTableColorFilter.h +++ b/gfx/skia/skia/include/effects/SkTableColorFilter.h @@ -23,7 +23,7 @@ public: * colors are premultiplied, they are temporarily unpremultiplied, then * the table is applied, and then the result is remultiplied. */ - static SkColorFilter* Create(const uint8_t table[256]); + static sk_sp Make(const uint8_t table[256]); /** * Create a table colorfilter, with a different table for each @@ -31,11 +31,23 @@ public: * treated as identity, with the component left unchanged. If a table * is not null, then its contents are copied into the filter. */ + static sk_sp MakeARGB(const uint8_t tableA[256], + const uint8_t tableR[256], + const uint8_t tableG[256], + const uint8_t tableB[256]); + +#ifdef SK_SUPPORT_LEGACY_COLORFILTER_PTR + static SkColorFilter* Create(const uint8_t table[256]) { + return Make(table).release(); + } static SkColorFilter* CreateARGB(const uint8_t tableA[256], const uint8_t tableR[256], const uint8_t tableG[256], - const uint8_t tableB[256]); - + const uint8_t tableB[256]) { + return MakeARGB(tableA, tableR, tableG, tableB).release(); + } +#endif + SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() }; diff --git a/gfx/skia/skia/include/effects/SkTestImageFilters.h b/gfx/skia/skia/include/effects/SkTestImageFilters.h index 06a3ff31a1..0f89759c1c 100644 --- a/gfx/skia/skia/include/effects/SkTestImageFilters.h +++ b/gfx/skia/skia/include/effects/SkTestImageFilters.h @@ -14,28 +14,35 @@ // Fun mode that scales down (only) and then scales back up to look pixelated class SK_API SkDownSampleImageFilter : public SkImageFilter { public: - static SkImageFilter* Create(SkScalar scale, SkImageFilter* input = NULL) { + static sk_sp Make(SkScalar scale, sk_sp input) { if (!SkScalarIsFinite(scale)) { - return NULL; + return nullptr; } // we don't support scale in this range if (scale > SK_Scalar1 || scale <= 0) { - return NULL; + return nullptr; } - return new SkDownSampleImageFilter(scale, input); + return sk_sp(new SkDownSampleImageFilter(scale, std::move(input))); } SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDownSampleImageFilter) +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static SkImageFilter* Create(SkScalar scale, SkImageFilter* input = nullptr) { + return Make(scale, sk_ref_sp(input)).release(); + } +#endif + protected: void flatten(SkWriteBuffer&) const override; - bool onFilterImage(Proxy*, const SkBitmap& src, const Context&, SkBitmap* result, - SkIPoint* loc) const override; + + sk_sp onFilterImage(SkSpecialImage* source, const Context&, + SkIPoint* offset) const override; private: - SkDownSampleImageFilter(SkScalar scale, SkImageFilter* input) - : INHERITED(1, &input), fScale(scale) {} + SkDownSampleImageFilter(SkScalar scale, sk_sp input) + : INHERITED(&input, 1, nullptr), fScale(scale) {} SkScalar fScale; diff --git a/gfx/skia/skia/include/effects/SkTileImageFilter.h b/gfx/skia/skia/include/effects/SkTileImageFilter.h index ea75a3ec7f..ad334a3cc1 100644 --- a/gfx/skia/skia/include/effects/SkTileImageFilter.h +++ b/gfx/skia/skia/include/effects/SkTileImageFilter.h @@ -21,12 +21,11 @@ public: */ static SkImageFilter* Create(const SkRect& src, const SkRect& dst, SkImageFilter* input); - bool onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx, - SkBitmap* dst, SkIPoint* offset) const override; - bool onFilterBounds(const SkIRect& src, const SkMatrix&, - SkIRect* dst) const override; - void onFilterNodeBounds(const SkIRect&, const SkMatrix&, SkIRect*, MapDirection) const override; - void computeFastBounds(const SkRect& src, SkRect* dst) const override; + bool onFilterImageDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, + SkBitmap* dst, SkIPoint* offset) const override; + SkIRect onFilterBounds(const SkIRect& src, const SkMatrix&, MapDirection) const override; + SkIRect onFilterNodeBounds(const SkIRect&, const SkMatrix&, MapDirection) const override; + SkRect computeFastBounds(const SkRect& src) const override; SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTileImageFilter) diff --git a/gfx/skia/skia/include/effects/SkXfermodeImageFilter.h b/gfx/skia/skia/include/effects/SkXfermodeImageFilter.h index d59b7a2b25..634422747a 100644 --- a/gfx/skia/skia/include/effects/SkXfermodeImageFilter.h +++ b/gfx/skia/skia/include/effects/SkXfermodeImageFilter.h @@ -21,36 +21,56 @@ class SK_API SkXfermodeImageFilter : public SkImageFilter { */ public: - virtual ~SkXfermodeImageFilter(); + static sk_sp Make(sk_sp mode, sk_sp background, + sk_sp foreground, const CropRect* cropRect); + static sk_sp Make(sk_sp mode, sk_sp background) { + return Make(std::move(mode), std::move(background), nullptr, nullptr); + } +#ifdef SK_SUPPORT_LEGACY_XFERMODE_PTR static SkImageFilter* Create(SkXfermode* mode, SkImageFilter* background, SkImageFilter* foreground = NULL, const CropRect* cropRect = NULL) { - SkImageFilter* inputs[2] = { background, foreground }; - return new SkXfermodeImageFilter(mode, inputs, cropRect); + return Make(sk_ref_sp(mode), + sk_ref_sp(background), + sk_ref_sp(foreground), + cropRect).release(); } +#endif +#ifdef SK_SUPPORT_LEGACY_IMAGEFILTER_PTR + static sk_sp Make(sk_sp mode, SkImageFilter* background, + SkImageFilter* foreground, const CropRect* cropRect) { + return Make(std::move(mode), + sk_ref_sp(background), + sk_ref_sp(foreground), + cropRect); + } + static sk_sp Make(sk_sp mode, SkImageFilter* background) { + return Make(std::move(mode), sk_ref_sp(background)); + } +#endif SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkXfermodeImageFilter) - bool onFilterImage(Proxy* proxy, - const SkBitmap& src, - const Context& ctx, - SkBitmap* dst, - SkIPoint* offset) const override; + bool onFilterImageDeprecated(Proxy* proxy, + const SkBitmap& src, + const Context& ctx, + SkBitmap* dst, + SkIPoint* offset) const override; #if SK_SUPPORT_GPU bool canFilterImageGPU() const override; - bool filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, - SkBitmap* result, SkIPoint* offset) const override; + bool filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, + SkBitmap* result, SkIPoint* offset) const override; #endif protected: - SkXfermodeImageFilter(SkXfermode* mode, SkImageFilter* inputs[2], + SkXfermodeImageFilter(sk_sp mode, sk_sp inputs[2], const CropRect* cropRect); void flatten(SkWriteBuffer&) const override; private: - SkXfermode* fMode; + sk_sp fMode; typedef SkImageFilter INHERITED; }; diff --git a/gfx/skia/skia/include/gpu/GrCaps.h b/gfx/skia/skia/include/gpu/GrCaps.h index 679777f9dd..d698a4142c 100644 --- a/gfx/skia/skia/include/gpu/GrCaps.h +++ b/gfx/skia/skia/include/gpu/GrCaps.h @@ -62,6 +62,7 @@ public: bool pathRenderingSupport() const { return fPathRenderingSupport; } bool dstReadInShaderSupport() const { return fDstReadInShaderSupport; } bool dualSourceBlendingSupport() const { return fDualSourceBlendingSupport; } + bool integerSupport() const { return fIntegerSupport; } /** * Get the precision info for a variable of type kFloat_GrSLType, kVec2f_GrSLType, etc in a @@ -81,6 +82,24 @@ public: */ bool floatPrecisionVaries() const { return fShaderPrecisionVaries; } + /** + * PLS storage size in bytes (0 when not supported). The PLS spec defines a minimum size of 16 + * bytes whenever PLS is supported. + */ + int pixelLocalStorageSize() const { return fPixelLocalStorageSize; } + + /** + * True if this context supports the necessary extensions and features to enable the PLS path + * renderer. + */ + bool plsPathRenderingSupport() const { +#if GR_ENABLE_PLS_PATH_RENDERING + return fPLSPathRenderingSupport; +#else + return false; +#endif + } + protected: /** Subclasses must call this after initialization in order to apply caps overrides requested by the client. Note that overrides will only reduce the caps never expand them. */ @@ -91,9 +110,12 @@ protected: bool fPathRenderingSupport : 1; bool fDstReadInShaderSupport : 1; bool fDualSourceBlendingSupport : 1; + bool fIntegerSupport : 1; bool fShaderPrecisionVaries; PrecisionInfo fFloatPrecisions[kGrShaderTypeCount][kGrSLPrecisionCount]; + int fPixelLocalStorageSize; + bool fPLSPathRenderingSupport; private: virtual void onApplyOptionsOverrides(const GrContextOptions&) {}; @@ -115,6 +137,14 @@ public: /** To avoid as-yet-unnecessary complexity we don't allow any partial support of MIP Maps (e.g. only for POT textures) */ bool mipMapSupport() const { return fMipMapSupport; } + + /** + * Skia convention is that a device only has sRGB support if it supports sRGB formats for both + * textures and framebuffers. In addition: + * Decoding to linear of an sRGB texture can be disabled. + * Encoding and gamma-correct blending to an sRGB framebuffer can be disabled. + */ + bool srgbSupport() const { return fSRGBSupport; } bool twoSidedStencilSupport() const { return fTwoSidedStencilSupport; } bool stencilWrapOpsSupport() const { return fStencilWrapOpsSupport; } bool discardRenderTargetSupport() const { return fDiscardRenderTargetSupport; } @@ -122,13 +152,18 @@ public: bool compressedTexSubImageSupport() const { return fCompressedTexSubImageSupport; } bool oversizedStencilSupport() const { return fOversizedStencilSupport; } bool textureBarrierSupport() const { return fTextureBarrierSupport; } - bool mixedSamplesSupport() const { return fMixedSamplesSupport; } + bool sampleLocationsSupport() const { return fSampleLocationsSupport; } + bool usesMixedSamples() const { return fUsesMixedSamples; } bool useDrawInsteadOfClear() const { return fUseDrawInsteadOfClear; } bool useDrawInsteadOfPartialRenderTargetWrite() const { return fUseDrawInsteadOfPartialRenderTargetWrite; } + bool useDrawInsteadOfAllRenderTargetWrites() const { + return fUseDrawInsteadOfAllRenderTargetWrites; + } + bool preferVRAMUseOverFlushes() const { return fPreferVRAMUseOverFlushes; } /** @@ -181,6 +216,9 @@ public: bool reuseScratchTextures() const { return fReuseScratchTextures; } bool reuseScratchBuffers() const { return fReuseScratchBuffers; } + /// maximum number of attribute values per vertex + int maxVertexAttributes() const { return fMaxVertexAttributes; } + int maxRenderTargetSize() const { return fMaxRenderTargetSize; } int maxTextureSize() const { return fMaxTextureSize; } /** This is the maximum tile size to use by GPU devices for rendering sw-backed images/bitmaps. @@ -188,7 +226,22 @@ public: int maxTileSize() const { SkASSERT(fMaxTileSize <= fMaxTextureSize); return fMaxTileSize; } // Will be 0 if MSAA is not supported - int maxSampleCount() const { return fMaxSampleCount; } + int maxColorSampleCount() const { return fMaxColorSampleCount; } + // Will be 0 if MSAA is not supported + int maxStencilSampleCount() const { return fMaxStencilSampleCount; } + // Will be 0 if raster multisample is not supported. Raster multisample is a special HW mode + // where the rasterizer runs with more samples than are in the target framebuffer. + int maxRasterSamples() const { return fMaxRasterSamples; } + // We require the sample count to be less than maxColorSampleCount and maxStencilSampleCount. + // If we are using mixed samples, we only care about stencil. + int maxSampleCount() const { + if (this->usesMixedSamples()) { + return this->maxStencilSampleCount(); + } else { + return SkTMin(this->maxColorSampleCount(), this->maxStencilSampleCount()); + } + } + virtual bool isConfigTexturable(GrPixelConfig config) const = 0; virtual bool isConfigRenderable(GrPixelConfig config, bool withMSAA) const = 0; @@ -201,9 +254,9 @@ public: return fDrawPathMasksToCompressedTextureSupport; } - size_t geometryBufferMapThreshold() const { - SkASSERT(fGeometryBufferMapThreshold >= 0); - return fGeometryBufferMapThreshold; + size_t bufferMapThreshold() const { + SkASSERT(fBufferMapThreshold >= 0); + return fBufferMapThreshold; } bool supportsInstancedDraws() const { @@ -216,6 +269,8 @@ public: is not initialized (even if not read by draw calls). */ bool mustClearUploadedBufferData() const { return fMustClearUploadedBufferData; } + bool sampleShadingSupport() const { return fSampleShadingSupport; } + protected: /** Subclasses must call this at the end of their constructors in order to apply caps overrides requested by the client. Note that overrides will only reduce the caps never @@ -226,6 +281,7 @@ protected: bool fNPOTTextureTileSupport : 1; bool fMipMapSupport : 1; + bool fSRGBSupport : 1; bool fTwoSidedStencilSupport : 1; bool fStencilWrapOpsSupport : 1; bool fDiscardRenderTargetSupport : 1; @@ -235,7 +291,8 @@ protected: bool fCompressedTexSubImageSupport : 1; bool fOversizedStencilSupport : 1; bool fTextureBarrierSupport : 1; - bool fMixedSamplesSupport : 1; + bool fSampleLocationsSupport : 1; + bool fUsesMixedSamples : 1; bool fSupportsInstancedDraws : 1; bool fFullClearIsFree : 1; bool fMustClearUploadedBufferData : 1; @@ -243,21 +300,27 @@ protected: // Driver workaround bool fUseDrawInsteadOfClear : 1; bool fUseDrawInsteadOfPartialRenderTargetWrite : 1; + bool fUseDrawInsteadOfAllRenderTargetWrites : 1; // ANGLE workaround bool fPreferVRAMUseOverFlushes : 1; + bool fSampleShadingSupport : 1; + BlendEquationSupport fBlendEquationSupport; uint32_t fAdvBlendEqBlacklist; GR_STATIC_ASSERT(kLast_GrBlendEquation < 32); uint32_t fMapBufferFlags; - int fGeometryBufferMapThreshold; + int fBufferMapThreshold; int fMaxRenderTargetSize; + int fMaxVertexAttributes; int fMaxTextureSize; int fMaxTileSize; - int fMaxSampleCount; + int fMaxColorSampleCount; + int fMaxStencilSampleCount; + int fMaxRasterSamples; private: virtual void onApplyOptionsOverrides(const GrContextOptions&) {}; diff --git a/gfx/skia/skia/include/gpu/GrClip.h b/gfx/skia/skia/include/gpu/GrClip.h index cf6e65c976..fd8b970e39 100644 --- a/gfx/skia/skia/include/gpu/GrClip.h +++ b/gfx/skia/skia/include/gpu/GrClip.h @@ -114,6 +114,13 @@ public: } } + void setIRect(const SkIRect& irect) { + this->reset(); + fClipType = kIRect_ClipType; + fOrigin.setZero(); + fClip.fIRect = irect; + } + const SkIRect& irect() const { SkASSERT(kIRect_ClipType == fClipType); return fClip.fIRect; diff --git a/gfx/skia/skia/include/gpu/GrColor.h b/gfx/skia/skia/include/gpu/GrColor.h index 6b83237626..77f1a6cb08 100644 --- a/gfx/skia/skia/include/gpu/GrColor.h +++ b/gfx/skia/skia/include/gpu/GrColor.h @@ -208,7 +208,6 @@ static inline char GrColorComponentFlagToChar(GrColorComponentFlags component) { } static inline uint32_t GrPixelConfigComponentMask(GrPixelConfig config) { - SkASSERT(config >= 0 && config < kGrPixelConfigCnt); static const uint32_t kFlags[] = { 0, // kUnknown_GrPixelConfig kA_GrColorComponentFlag, // kAlpha_8_GrPixelConfig @@ -218,6 +217,7 @@ static inline uint32_t GrPixelConfigComponentMask(GrPixelConfig config) { kRGBA_GrColorComponentFlags, // kRGBA_8888_GrPixelConfig kRGBA_GrColorComponentFlags, // kBGRA_8888_GrPixelConfig kRGBA_GrColorComponentFlags, // kSRGBA_8888_GrPixelConfig + kRGBA_GrColorComponentFlags, // kSBGRA_8888_GrPixelConfig kRGB_GrColorComponentFlags, // kETC1_GrPixelConfig kA_GrColorComponentFlag, // kLATC_GrPixelConfig kA_GrColorComponentFlag, // kR11_EAC_GrPixelConfig @@ -236,13 +236,14 @@ static inline uint32_t GrPixelConfigComponentMask(GrPixelConfig config) { GR_STATIC_ASSERT(5 == kRGBA_8888_GrPixelConfig); GR_STATIC_ASSERT(6 == kBGRA_8888_GrPixelConfig); GR_STATIC_ASSERT(7 == kSRGBA_8888_GrPixelConfig); - GR_STATIC_ASSERT(8 == kETC1_GrPixelConfig); - GR_STATIC_ASSERT(9 == kLATC_GrPixelConfig); - GR_STATIC_ASSERT(10 == kR11_EAC_GrPixelConfig); - GR_STATIC_ASSERT(11 == kASTC_12x12_GrPixelConfig); - GR_STATIC_ASSERT(12 == kRGBA_float_GrPixelConfig); - GR_STATIC_ASSERT(13 == kAlpha_half_GrPixelConfig); - GR_STATIC_ASSERT(14 == kRGBA_half_GrPixelConfig); + GR_STATIC_ASSERT(8 == kSBGRA_8888_GrPixelConfig); + GR_STATIC_ASSERT(9 == kETC1_GrPixelConfig); + GR_STATIC_ASSERT(10 == kLATC_GrPixelConfig); + GR_STATIC_ASSERT(11 == kR11_EAC_GrPixelConfig); + GR_STATIC_ASSERT(12 == kASTC_12x12_GrPixelConfig); + GR_STATIC_ASSERT(13 == kRGBA_float_GrPixelConfig); + GR_STATIC_ASSERT(14 == kAlpha_half_GrPixelConfig); + GR_STATIC_ASSERT(15 == kRGBA_half_GrPixelConfig); GR_STATIC_ASSERT(SK_ARRAY_COUNT(kFlags) == kGrPixelConfigCnt); } diff --git a/gfx/skia/skia/include/gpu/GrConfig.h b/gfx/skia/skia/include/gpu/GrConfig.h index 34666857e5..8e0efbd472 100644 --- a/gfx/skia/skia/include/gpu/GrConfig.h +++ b/gfx/skia/skia/include/gpu/GrConfig.h @@ -33,7 +33,7 @@ */ #if !defined(GR_CACHE_STATS) - #if defined(SK_DEVELOPER) || defined(SK_DUMP_STATS) + #if defined(SK_DEVELOPER) || defined(SK_DEBUG) || defined(SK_DUMP_STATS) #define GR_CACHE_STATS 1 #else #define GR_CACHE_STATS 0 @@ -41,7 +41,7 @@ #endif #if !defined(GR_GPU_STATS) - #if defined(SK_DEVELOPER) || defined(SK_DUMP_STATS) + #if defined(SK_DEVELOPER) || defined(SK_DEBUG) || defined(SK_DUMP_STATS) #define GR_GPU_STATS 1 #else #define GR_GPU_STATS 0 @@ -106,7 +106,7 @@ typedef unsigned __int64 uint64_t; * particular compiler. * To insert compiler warnings use "#pragma message GR_WARN()" */ -#if defined(_MSC_VER) && _MSC_VER +#if defined(_MSC_VER) #define GR_WARN(MSG) (GR_FILE_AND_LINE_STR "WARNING: " MSG) #else//__GNUC__ - may need other defines for different compilers #define GR_WARN(MSG) ("WARNING: " MSG) @@ -171,26 +171,13 @@ typedef unsigned __int64 uint64_t; * it may print the message in the compiler log. Obviously, the condition must * be evaluatable at compile time. */ -// VS 2010 and GCC compiled with c++0x or gnu++0x support the new -// static_assert. -#if !defined(GR_STATIC_ASSERT) - #if (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__GXX_EXPERIMENTAL_CXX0X__) && __GXX_EXPERIMENTAL_CXX0X__) - #define GR_STATIC_ASSERT(CONDITION) static_assert(CONDITION, "bug") - #else - template class GR_STATIC_ASSERT_FAILURE; - template <> class GR_STATIC_ASSERT_FAILURE {}; - #define GR_STATIC_ASSERT(CONDITION) \ - enum {GR_CONCAT(X,__LINE__) = \ - sizeof(GR_STATIC_ASSERT_FAILURE)} - #endif -#endif +#define GR_STATIC_ASSERT(CONDITION) static_assert(CONDITION, "bug") /** - * Enable batch debugging output as json. The enabler of this flag is responsible for making sure - * GrAuditTrail is reset occasionally. - * TODO make this runtime configurable + * Set to 1 to enable pixel local storage path rendering on supported devices. */ -#if !defined(GR_BATCH_DEBUGGING_OUTPUT) - #define GR_BATCH_DEBUGGING_OUTPUT 0 +#if !defined(GR_ENABLE_PLS_PATH_RENDERING) + #define GR_ENABLE_PLS_PATH_RENDERING 0 #endif + #endif diff --git a/gfx/skia/skia/include/gpu/GrContext.h b/gfx/skia/skia/include/gpu/GrContext.h index b60aea0813..d59b2a28f0 100644 --- a/gfx/skia/skia/include/gpu/GrContext.h +++ b/gfx/skia/skia/include/gpu/GrContext.h @@ -24,6 +24,7 @@ struct GrBatchAtlasConfig; class GrBatchFontCache; struct GrContextOptions; +class GrContextThreadSafeProxy; class GrDrawingManager; class GrDrawContext; class GrDrawTarget; @@ -43,6 +44,7 @@ class GrTextContext; class GrTextureParams; class GrVertexBuffer; class GrStrokeInfo; +class GrSwizzle; class SkTraceMemoryDump; class SK_API GrContext : public SkRefCnt { @@ -60,6 +62,8 @@ public: virtual ~GrContext(); + GrContextThreadSafeProxy* threadSafeProxy(); + /** * The GrContext normally assumes that no outsider is setting state * within the underlying 3D API's context/device/whatever. This call informs @@ -89,21 +93,29 @@ public: } /** - * Abandons all GPU resources and assumes the underlying backend 3D API - * context is not longer usable. Call this if you have lost the associated - * GPU context, and thus internal texture, buffer, etc. references/IDs are - * now invalid. Should be called even when GrContext is no longer going to - * be used for two reasons: - * 1) ~GrContext will not try to free the objects in the 3D API. - * 2) Any GrGpuResources created by this GrContext that outlive - * will be marked as invalid (GrGpuResource::wasDestroyed()) and - * when they're destroyed no 3D API calls will be made. - * Content drawn since the last GrContext::flush() may be lost. After this - * function is called the only valid action on the GrContext or - * GrGpuResources it created is to destroy them. + * Abandons all GPU resources and assumes the underlying backend 3D API context is not longer + * usable. Call this if you have lost the associated GPU context, and thus internal texture, + * buffer, etc. references/IDs are now invalid. Calling this ensures that the destructors of the + * GrContext and any of its created resource objects will not make backend 3D API calls. Content + * rendered but not previously flushed may be lost. After this function is called all subsequent + * calls on the GrContext will fail or be no-ops. + * + * The typical use case for this function is that the underlying 3D context was lost and further + * API calls may crash. */ void abandonContext(); + /** + * This is similar to abandonContext() however the underlying 3D context is not yet lost and + * the GrContext will cleanup all allocated resources before returning. After returning it will + * assume that the underlying context may no longer be valid. + * + * The typical use case for this function is that the client is going to destroy the 3D context + * but can't guarantee that GrContext will be destroyed first (perhaps because it may be ref'ed + * elsewhere by either the client or Skia objects). + */ + void releaseResourcesAndAbandonContext(); + /////////////////////////////////////////////////////////////////////////// // Resource Cache @@ -277,24 +289,17 @@ public: * @param src the surface to copy from. * @param srcRect the rectangle of the src that should be copied. * @param dstPoint the translation applied when writing the srcRect's pixels to the dst. - * @param pixelOpsFlags see PixelOpsFlags enum above. (kUnpremul_PixelOpsFlag is not allowed). */ - void copySurface(GrSurface* dst, + bool copySurface(GrSurface* dst, GrSurface* src, const SkIRect& srcRect, - const SkIPoint& dstPoint, - uint32_t pixelOpsFlags = 0); + const SkIPoint& dstPoint); /** Helper that copies the whole surface but fails when the two surfaces are not identically sized. */ bool copySurface(GrSurface* dst, GrSurface* src) { - if (NULL == dst || NULL == src || dst->width() != src->width() || - dst->height() != src->height()) { - return false; - } - this->copySurface(dst, src, SkIRect::MakeWH(dst->width(), dst->height()), - SkIPoint::Make(0,0)); - return true; + return this->copySurface(dst, src, SkIRect::MakeWH(dst->width(), dst->height()), + SkIPoint::Make(0,0)); } /** @@ -365,25 +370,27 @@ public: SkDEBUGCODE(GrSingleOwner* debugSingleOwner() const { return &fSingleOwner; } ) private: - GrGpu* fGpu; - const GrCaps* fCaps; - GrResourceCache* fResourceCache; + GrGpu* fGpu; + const GrCaps* fCaps; + GrResourceCache* fResourceCache; // this union exists because the inheritance of GrTextureProvider->GrResourceProvider // is in a private header. union { - GrResourceProvider* fResourceProvider; - GrTextureProvider* fTextureProvider; + GrResourceProvider* fResourceProvider; + GrTextureProvider* fTextureProvider; }; - GrBatchFontCache* fBatchFontCache; - SkAutoTDelete fLayerCache; - SkAutoTDelete fTextBlobCache; + SkAutoTUnref fThreadSafeProxy; + + GrBatchFontCache* fBatchFontCache; + SkAutoTDelete fLayerCache; + SkAutoTDelete fTextBlobCache; // Set by OverbudgetCB() to request that GrContext flush before exiting a draw. - bool fFlushToReduceCacheSize; - bool fDidTestPMConversions; - int fPMToUPMConversion; - int fUPMToPMConversion; + bool fFlushToReduceCacheSize; + bool fDidTestPMConversions; + int fPMToUPMConversion; + int fUPMToPMConversion; // The sw backend may call GrContext::readSurfacePixels on multiple threads // We may transfer the responsibilty for using a mutex to the sw backend // when there are fewer code paths that lead to a readSurfacePixels call @@ -394,26 +401,26 @@ private: // readSurfacePixels proceeds to grab it. // TODO: Stop pretending to make GrContext thread-safe for sw rasterization and provide // a mechanism to make a SkPicture safe for multithreaded sw rasterization. - SkMutex fReadPixelsMutex; - SkMutex fTestPMConversionsMutex; + SkMutex fReadPixelsMutex; + SkMutex fTestPMConversionsMutex; // In debug builds we guard against improper thread handling // This guard is passed to the GrDrawingManager and, from there to all the // GrDrawContexts. It is also passed to the GrTextureProvider and SkGpuDevice. - mutable GrSingleOwner fSingleOwner; + mutable GrSingleOwner fSingleOwner; struct CleanUpData { PFCleanUpFunc fFunc; void* fInfo; }; - SkTDArray fCleanUpData; + SkTDArray fCleanUpData; - const uint32_t fUniqueID; + const uint32_t fUniqueID; - SkAutoTDelete fDrawingManager; + SkAutoTDelete fDrawingManager; - GrAuditTrail fAuditTrail; + GrAuditTrail fAuditTrail; // TODO: have the CMM use drawContexts and rm this friending friend class GrClipMaskManager; // the CMM is friended just so it can call 'drawingManager' @@ -429,11 +436,11 @@ private: /** * These functions create premul <-> unpremul effects if it is possible to generate a pair * of effects that make a readToUPM->writeToPM->readToUPM cycle invariant. Otherwise, they - * return NULL. + * return NULL. They also can perform a swizzle as part of the draw. */ - const GrFragmentProcessor* createPMToUPMEffect(GrTexture*, bool swapRAndB, + const GrFragmentProcessor* createPMToUPMEffect(GrTexture*, const GrSwizzle&, const SkMatrix&) const; - const GrFragmentProcessor* createUPMToPMEffect(GrTexture*, bool swapRAndB, + const GrFragmentProcessor* createUPMToPMEffect(GrTexture*, const GrSwizzle&, const SkMatrix&) const; /** Called before either of the above two functions to determine the appropriate fragment processors for conversions. This must be called by readSurfacePixels before a mutex is @@ -458,4 +465,23 @@ private: typedef SkRefCnt INHERITED; }; +/** + * Can be used to perform actions related to the generating GrContext in a thread safe manner. The + * proxy does not access the 3D API (e.g. OpenGL) that backs the generating GrContext. + */ +class GrContextThreadSafeProxy : public SkRefCnt { +private: + GrContextThreadSafeProxy(const GrCaps* caps, uint32_t uniqueID) + : fCaps(SkRef(caps)) + , fContextUniqueID(uniqueID) {} + + SkAutoTUnref fCaps; + uint32_t fContextUniqueID; + + friend class GrContext; + friend class SkImage; + + typedef SkRefCnt INHERITED; +}; + #endif diff --git a/gfx/skia/skia/include/gpu/GrContextOptions.h b/gfx/skia/skia/include/gpu/GrContextOptions.h index 8e6368a389..4e763405e1 100644 --- a/gfx/skia/skia/include/gpu/GrContextOptions.h +++ b/gfx/skia/skia/include/gpu/GrContextOptions.h @@ -17,12 +17,13 @@ struct GrContextOptions { , fMaxTextureSizeOverride(SK_MaxS32) , fMaxTileSizeOverride(0) , fSuppressDualSourceBlending(false) - , fGeometryBufferMapThreshold(-1) + , fBufferMapThreshold(-1) , fUseDrawInsteadOfPartialRenderTargetWrite(false) , fImmediateMode(false) , fClipBatchToBounds(false) , fDrawBatchBounds(false) , fMaxBatchLookback(-1) + , fMaxBatchLookahead(-1) , fUseShaderSwizzling(false) {} // EXPERIMENTAL @@ -46,7 +47,7 @@ struct GrContextOptions { /** the threshold in bytes above which we will use a buffer mapping API to map vertex and index buffers to CPU memory in order to update them. A value of -1 means the GrContext should deduce the optimal value for this platform. */ - int fGeometryBufferMapThreshold; + int fBufferMapThreshold; /** some gpus have problems with partial writes of the rendertarget */ bool fUseDrawInsteadOfPartialRenderTargetWrite; @@ -64,8 +65,10 @@ struct GrContextOptions { of their dev bounds. */ bool fDrawBatchBounds; - /** For debugging, override the default maximum look-back window for GrBatch combining. */ + /** For debugging, override the default maximum look-back or look-ahead window for GrBatch + combining. */ int fMaxBatchLookback; + int fMaxBatchLookahead; /** Force us to do all swizzling manually in the shader and don't rely on extensions to do swizzling. */ diff --git a/gfx/skia/skia/include/gpu/GrDrawContext.h b/gfx/skia/skia/include/gpu/GrDrawContext.h index 9276e2b21f..06954dc1bb 100644 --- a/gfx/skia/skia/include/gpu/GrDrawContext.h +++ b/gfx/skia/skia/include/gpu/GrDrawContext.h @@ -11,13 +11,16 @@ #include "GrColor.h" #include "GrRenderTarget.h" #include "SkRefCnt.h" +#include "SkRegion.h" #include "SkSurfaceProps.h" #include "../private/GrSingleOwner.h" +class GrAtlasTextContext; class GrAuditTrail; class GrClip; class GrContext; class GrDrawBatch; +class GrDrawContextPriv; class GrDrawPathBatchBase; class GrDrawingManager; class GrDrawTarget; @@ -27,7 +30,6 @@ class GrPipelineBuilder; class GrRenderTarget; class GrStrokeInfo; class GrSurface; -class GrTextContext; class SkDrawFilter; struct SkIPoint; struct SkIRect; @@ -47,22 +49,22 @@ class SK_API GrDrawContext : public SkRefCnt { public: ~GrDrawContext() override; - void copySurface(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint); + bool copySurface(GrSurface* src, const SkIRect& srcRect, const SkIPoint& dstPoint); // TODO: it is odd that we need both the SkPaint in the following 3 methods. // We should extract the text parameters from SkPaint and pass them separately // akin to GrStrokeInfo (GrTextInfo?) - void drawText(const GrClip&, const GrPaint&, const SkPaint&, - const SkMatrix& viewMatrix, const char text[], size_t byteLength, - SkScalar x, SkScalar y, const SkIRect& clipBounds); - void drawPosText(const GrClip&, const GrPaint&, const SkPaint&, - const SkMatrix& viewMatrix, const char text[], size_t byteLength, - const SkScalar pos[], int scalarsPerPosition, - const SkPoint& offset, const SkIRect& clipBounds); - void drawTextBlob(const GrClip&, const SkPaint&, - const SkMatrix& viewMatrix, const SkTextBlob*, - SkScalar x, SkScalar y, - SkDrawFilter*, const SkIRect& clipBounds); + virtual void drawText(const GrClip&, const GrPaint&, const SkPaint&, + const SkMatrix& viewMatrix, const char text[], size_t byteLength, + SkScalar x, SkScalar y, const SkIRect& clipBounds); + virtual void drawPosText(const GrClip&, const GrPaint&, const SkPaint&, + const SkMatrix& viewMatrix, const char text[], size_t byteLength, + const SkScalar pos[], int scalarsPerPosition, + const SkPoint& offset, const SkIRect& clipBounds); + virtual void drawTextBlob(const GrClip&, const SkPaint&, + const SkMatrix& viewMatrix, const SkTextBlob*, + SkScalar x, SkScalar y, + SkDrawFilter*, const SkIRect& clipBounds); /** * Provides a perfomance hint that the render target's contents are allowed @@ -100,7 +102,7 @@ public: const GrPaint& paint, const SkMatrix& viewMatrix, const SkRect&, - const GrStrokeInfo* strokeInfo = NULL); + const GrStrokeInfo* strokeInfo = nullptr); /** * Maps a rectangle of shader coordinates to a rectangle and fills that rectangle. @@ -156,7 +158,6 @@ public: const SkRRect& outer, const SkRRect& inner); - /** * Draws a path. * @@ -275,28 +276,45 @@ public: int width() const { return fRenderTarget->width(); } int height() const { return fRenderTarget->height(); } int numColorSamples() const { return fRenderTarget->numColorSamples(); } + bool allowSRGBInputs() const { return fSurfaceProps.allowSRGBInputs(); } GrRenderTarget* accessRenderTarget() { return fRenderTarget; } - /////////////////////////////////////////////////////////////////////////////////////////////// - // Functions intended for internal use only. - void internal_drawBatch(const GrPipelineBuilder& pipelineBuilder, GrDrawBatch* batch); + // Provides access to functions that aren't part of the public API. + GrDrawContextPriv drawContextPriv(); + const GrDrawContextPriv drawContextPriv() const; + +protected: + GrDrawContext(GrContext*, GrDrawingManager*, GrRenderTarget*, + const SkSurfaceProps* surfaceProps, GrAuditTrail*, GrSingleOwner*); + + GrDrawingManager* drawingManager() { return fDrawingManager; } + GrAuditTrail* auditTrail() { return fAuditTrail; } + const SkSurfaceProps& surfaceProps() const { return fSurfaceProps; } + + SkDEBUGCODE(GrSingleOwner* singleOwner() { return fSingleOwner; }) + SkDEBUGCODE(void validate() const;) private: friend class GrAtlasTextBlob; // for access to drawBatch friend class GrDrawingManager; // for ctor + friend class GrDrawContextPriv; - SkDEBUGCODE(void validate() const;) - - GrDrawContext(GrDrawingManager*, GrRenderTarget*, const SkSurfaceProps* surfaceProps, - GrAuditTrail*, GrSingleOwner*); - - void internalDrawPath(GrPipelineBuilder*, + bool drawFilledDRRect(const GrClip& clip, + const GrPaint& paint, const SkMatrix& viewMatrix, - GrColor, - bool useAA, - const SkPath&, - const GrStrokeInfo&); + const SkRRect& origOuter, + const SkRRect& origInner); + + GrDrawBatch* getFillRectBatch(const GrPaint& paint, + const SkMatrix& viewMatrix, + const SkRect& rect); + + void internalDrawPath(const GrClip& clip, + const GrPaint& paint, + const SkMatrix& viewMatrix, + const SkPath& path, + const GrStrokeInfo& strokeInfo); // This entry point allows the GrTextContext-derived classes to add their batches to // the drawTarget. @@ -304,16 +322,17 @@ private: GrDrawTarget* getDrawTarget(); - GrDrawingManager* fDrawingManager; - GrRenderTarget* fRenderTarget; + GrDrawingManager* fDrawingManager; + GrRenderTarget* fRenderTarget; // In MDB-mode the drawTarget can be closed by some other drawContext that has picked // it up. For this reason, the drawTarget should only ever be accessed via 'getDrawTarget'. - GrDrawTarget* fDrawTarget; - GrTextContext* fTextContext; // lazily gotten from GrContext::DrawingManager + GrDrawTarget* fDrawTarget; + SkAutoTDelete fAtlasTextContext; + GrContext* fContext; - SkSurfaceProps fSurfaceProps; - GrAuditTrail* fAuditTrail; + SkSurfaceProps fSurfaceProps; + GrAuditTrail* fAuditTrail; // In debug builds we guard against improper thread handling SkDEBUGCODE(mutable GrSingleOwner* fSingleOwner;) diff --git a/gfx/skia/skia/include/gpu/GrPaint.h b/gfx/skia/skia/include/gpu/GrPaint.h index 152cb51d7e..87e0368655 100644 --- a/gfx/skia/skia/include/gpu/GrPaint.h +++ b/gfx/skia/skia/include/gpu/GrPaint.h @@ -56,6 +56,20 @@ public: void setAntiAlias(bool aa) { fAntiAlias = aa; } bool isAntiAlias() const { return fAntiAlias; } + /** + * Should shader output conversion from linear to sRGB be disabled. + * Only relevant if the destination is sRGB. Defaults to false. + */ + void setDisableOutputConversionToSRGB(bool srgb) { fDisableOutputConversionToSRGB = srgb; } + bool getDisableOutputConversionToSRGB() const { return fDisableOutputConversionToSRGB; } + + /** + * Should sRGB inputs be allowed to perform sRGB to linear conversion. With this flag + * set to false, sRGB textures will be treated as linear (including filtering). + */ + void setAllowSRGBInputs(bool allowSRGBInputs) { fAllowSRGBInputs = allowSRGBInputs; } + bool getAllowSRGBInputs() const { return fAllowSRGBInputs; } + const GrXPFactory* setXPFactory(const GrXPFactory* xpFactory) { fXPFactory.reset(SkSafeRef(xpFactory)); return xpFactory; @@ -112,6 +126,8 @@ public: GrPaint& operator=(const GrPaint& paint) { fAntiAlias = paint.fAntiAlias; + fDisableOutputConversionToSRGB = paint.fDisableOutputConversionToSRGB; + fAllowSRGBInputs = paint.fAllowSRGBInputs; fColor = paint.fColor; this->resetFragmentProcessors(); @@ -154,6 +170,8 @@ private: SkSTArray<2, const GrFragmentProcessor*, true> fCoverageFragmentProcessors; bool fAntiAlias; + bool fDisableOutputConversionToSRGB; + bool fAllowSRGBInputs; GrColor fColor; }; diff --git a/gfx/skia/skia/include/gpu/GrProcessor.h b/gfx/skia/skia/include/gpu/GrProcessor.h index 9b5db5e033..3e892c6e3e 100644 --- a/gfx/skia/skia/include/gpu/GrProcessor.h +++ b/gfx/skia/skia/include/gpu/GrProcessor.h @@ -79,8 +79,18 @@ public: /** Shortcut for textureAccess(index).texture(); */ GrTexture* texture(int index) const { return this->textureAccess(index).getTexture(); } - /** Will this processor read the fragment position? */ - bool willReadFragmentPosition() const { return fWillReadFragmentPosition; } + /** + * Platform specific built-in features that a processor can request for the fragment shader. + */ + enum RequiredFeatures { + kNone_RequiredFeatures = 0, + kFragmentPosition_RequiredFeature = 1 << 0, + kSampleLocations_RequiredFeature = 1 << 1 + }; + + GR_DECL_BITFIELD_OPS_FRIENDS(RequiredFeatures); + + RequiredFeatures requiredFeatures() const { return fRequiredFeatures; } void* operator new(size_t size); void operator delete(void* target); @@ -100,7 +110,7 @@ public: uint32_t classID() const { SkASSERT(kIllegalProcessorClassID != fClassID); return fClassID; } protected: - GrProcessor() : fClassID(kIllegalProcessorClassID), fWillReadFragmentPosition(false) {} + GrProcessor() : fClassID(kIllegalProcessorClassID), fRequiredFeatures(kNone_RequiredFeatures) {} /** * Subclasses call this from their constructor to register GrTextureAccesses. The processor @@ -113,11 +123,16 @@ protected: bool hasSameTextureAccesses(const GrProcessor&) const; /** - * If the prcoessor will generate a backend-specific processor that will read the fragment - * position in the FS then it must call this method from its constructor. Otherwise, the - * request to access the fragment position will be denied. + * If the prcoessor will generate code that uses platform specific built-in features, then it + * must call these methods from its constructor. Otherwise, requests to use these features will + * be denied. */ - void setWillReadFragmentPosition() { fWillReadFragmentPosition = true; } + void setWillReadFragmentPosition() { fRequiredFeatures |= kFragmentPosition_RequiredFeature; } + void setWillUseSampleLocations() { fRequiredFeatures |= kSampleLocations_RequiredFeature; } + + void combineRequiredFeatures(const GrProcessor& other) { + fRequiredFeatures |= other.fRequiredFeatures; + } template void initClassID() { static uint32_t kClassID = GenClassID(); @@ -145,9 +160,11 @@ private: }; static int32_t gCurrProcessorClassID; - bool fWillReadFragmentPosition; + RequiredFeatures fRequiredFeatures; typedef GrProgramElement INHERITED; }; +GR_MAKE_BITFIELD_OPS(GrProcessor::RequiredFeatures); + #endif diff --git a/gfx/skia/skia/include/gpu/GrProcessorUnitTest.h b/gfx/skia/skia/include/gpu/GrProcessorUnitTest.h index bb16d69fd5..8442d7ef71 100644 --- a/gfx/skia/skia/include/gpu/GrProcessorUnitTest.h +++ b/gfx/skia/skia/include/gpu/GrProcessorUnitTest.h @@ -8,8 +8,8 @@ #ifndef GrProcessorUnitTest_DEFINED #define GrProcessorUnitTest_DEFINED +#include "../private/SkTArray.h" #include "GrTestUtils.h" -#include "SkTArray.h" #include "SkTypes.h" class SkMatrix; diff --git a/gfx/skia/skia/include/gpu/GrProgramElement.h b/gfx/skia/skia/include/gpu/GrProgramElement.h index 8305c5027b..ba9daf7154 100644 --- a/gfx/skia/skia/include/gpu/GrProgramElement.h +++ b/gfx/skia/skia/include/gpu/GrProgramElement.h @@ -8,8 +8,8 @@ #ifndef GrProgramElement_DEFINED #define GrProgramElement_DEFINED +#include "../private/SkTArray.h" #include "SkRefCnt.h" -#include "SkTArray.h" class GrGpuResourceRef; diff --git a/gfx/skia/skia/include/gpu/GrShaderVar.h b/gfx/skia/skia/include/gpu/GrShaderVar.h index 68ce34b8ef..3f56eaf70a 100644 --- a/gfx/skia/skia/include/gpu/GrShaderVar.h +++ b/gfx/skia/skia/include/gpu/GrShaderVar.h @@ -51,7 +51,7 @@ public: , fName(name) , fCount(arrayCount) , fPrecision(precision) { - SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type)); + SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsNumeric(type)); SkASSERT(kVoid_GrSLType != type); } @@ -62,7 +62,7 @@ public: , fName(name) , fCount(arrayCount) , fPrecision(precision) { - SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type)); + SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsNumeric(type)); SkASSERT(kVoid_GrSLType != type); } @@ -73,7 +73,7 @@ public: , fName(name) , fCount(arrayCount) , fPrecision(precision) { - SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type)); + SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsNumeric(type)); SkASSERT(kVoid_GrSLType != type); } @@ -91,7 +91,7 @@ public: GrSLPrecision precision = kDefault_GrSLPrecision, int count = kNonArray) { SkASSERT(kVoid_GrSLType != type); - SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type)); + SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsNumeric(type)); fType = type; fTypeModifier = typeModifier; fName = name; @@ -105,7 +105,7 @@ public: GrSLPrecision precision = kDefault_GrSLPrecision, int count = kNonArray) { SkASSERT(kVoid_GrSLType != type); - SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type)); + SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsNumeric(type)); fType = type; fTypeModifier = typeModifier; fName = name; diff --git a/gfx/skia/skia/include/gpu/GrSurface.h b/gfx/skia/skia/include/gpu/GrSurface.h index 28935009ce..b59d802f32 100644 --- a/gfx/skia/skia/include/gpu/GrSurface.h +++ b/gfx/skia/skia/include/gpu/GrSurface.h @@ -100,8 +100,8 @@ public: * packed. * @param pixelOpsFlags See the GrContext::PixelOpsFlags enum. * - * @return true if the read succeeded, false if not. The read can fail because of an unsupported - * pixel config. + * @return true if the read succeeded, false if not. The read can fail because of an + * unsupported pixel config. */ bool writePixels(int left, int top, int width, int height, GrPixelConfig config, diff --git a/gfx/skia/skia/include/gpu/GrTestUtils.h b/gfx/skia/skia/include/gpu/GrTestUtils.h index 8ffae09ddd..475e38a6a1 100644 --- a/gfx/skia/skia/include/gpu/GrTestUtils.h +++ b/gfx/skia/skia/include/gpu/GrTestUtils.h @@ -51,7 +51,7 @@ static inline GrColor GrRandomColor(SkRandom* random) { }; ColorMode colorMode = ColorMode(random->nextULessThan(kLast_ColorMode + 1)); - GrColor color; + GrColor color SK_INIT_TO_AVOID_WARNING; switch (colorMode) { case kAllOnes_ColorMode: color = GrColorPackRGBA(0xFF, 0xFF, 0xFF, 0xFF); @@ -87,7 +87,7 @@ static inline uint8_t GrRandomCoverage(SkRandom* random) { }; CoverageMode colorMode = CoverageMode(random->nextULessThan(kLast_CoverageMode + 1)); - uint8_t coverage; + uint8_t coverage SK_INIT_TO_AVOID_WARNING; switch (colorMode) { case kZero_CoverageMode: coverage = 0; diff --git a/gfx/skia/skia/include/gpu/GrTexture.h b/gfx/skia/skia/include/gpu/GrTexture.h index ddba94065b..1d589eddce 100644 --- a/gfx/skia/skia/include/gpu/GrTexture.h +++ b/gfx/skia/skia/include/gpu/GrTexture.h @@ -20,6 +20,7 @@ class GrTexture : virtual public GrSurface { public: GrTexture* asTexture() override { return this; } const GrTexture* asTexture() const override { return this; } + GrSLType samplerType() const { return fSamplerType; } /** * Return the native ID or handle to the texture, depending on the @@ -45,7 +46,7 @@ public: inline const GrTexturePriv texturePriv() const; protected: - GrTexture(GrGpu*, LifeCycle, const GrSurfaceDesc&); + GrTexture(GrGpu*, LifeCycle, const GrSurfaceDesc&, GrSLType, bool wasMipMapDataProvided); void validateDesc() const; @@ -59,11 +60,9 @@ private: kValid_MipMapsStatus }; + GrSLType fSamplerType; MipMapsStatus fMipMapsStatus; - // These two shift a fixed-point value into normalized coordinates - // for this texture if the texture is power of two sized. - int fShiftFixedX; - int fShiftFixedY; + int fMaxMipMapLevel; friend class GrTexturePriv; diff --git a/gfx/skia/skia/include/gpu/GrTextureAccess.h b/gfx/skia/skia/include/gpu/GrTextureAccess.h index 124a75aabc..237485a9e2 100644 --- a/gfx/skia/skia/include/gpu/GrTextureAccess.h +++ b/gfx/skia/skia/include/gpu/GrTextureAccess.h @@ -29,20 +29,31 @@ public: explicit GrTextureAccess(GrTexture*, GrTextureParams::FilterMode = GrTextureParams::kNone_FilterMode, - SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode); + SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode, + GrShaderFlags visibility = kFragment_GrShaderFlag, + GrSLPrecision = kDefault_GrSLPrecision); - void reset(GrTexture*, const GrTextureParams&); + void reset(GrTexture*, const GrTextureParams&, + GrShaderFlags visibility = kFragment_GrShaderFlag, + GrSLPrecision = kDefault_GrSLPrecision); void reset(GrTexture*, GrTextureParams::FilterMode = GrTextureParams::kNone_FilterMode, - SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode); + SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode, + GrShaderFlags visibility = kFragment_GrShaderFlag, + GrSLPrecision = kDefault_GrSLPrecision); bool operator==(const GrTextureAccess& that) const { - return this->getTexture() == that.getTexture() && fParams == that.fParams; + return this->getTexture() == that.getTexture() && + fParams == that.fParams && + fVisibility == that.fVisibility && + fPrecision == that.fPrecision; } bool operator!=(const GrTextureAccess& other) const { return !(*this == other); } GrTexture* getTexture() const { return fTexture.get(); } + GrShaderFlags getVisibility() const { return fVisibility; } + GrSLPrecision getPrecision() const { return fPrecision; } /** * For internal use by GrProcessor. @@ -57,6 +68,8 @@ private: ProgramTexture fTexture; GrTextureParams fParams; + GrShaderFlags fVisibility; + GrSLPrecision fPrecision; typedef SkNoncopyable INHERITED; }; diff --git a/gfx/skia/skia/include/gpu/GrTextureParams.h b/gfx/skia/skia/include/gpu/GrTextureParams.h index 3186b1b027..40d9ac5375 100644 --- a/gfx/skia/skia/include/gpu/GrTextureParams.h +++ b/gfx/skia/skia/include/gpu/GrTextureParams.h @@ -24,6 +24,11 @@ public: static const GrTextureParams gParams(SkShader::kClamp_TileMode, kBilerp_FilterMode); return gParams; } + static const GrTextureParams& ClampNoFilterForceAllowSRGB() { + static const GrTextureParams gParams(SkShader::kClamp_TileMode, kNone_FilterMode, + kForceAllowSRGB_SRGBMode); + return gParams; + } GrTextureParams() { this->reset(); @@ -35,10 +40,19 @@ public: kMipMap_FilterMode }; + enum SRGBMode { + kRespectDestination_SRGBMode, + kForceAllowSRGB_SRGBMode, + }; + GrTextureParams(SkShader::TileMode tileXAndY, FilterMode filterMode) { this->reset(tileXAndY, filterMode); } + GrTextureParams(SkShader::TileMode tileXandY, FilterMode filterMode, SRGBMode srgbMode) { + this->reset(tileXandY, filterMode, srgbMode); + } + GrTextureParams(const SkShader::TileMode tileModes[2], FilterMode filterMode) { this->reset(tileModes, filterMode); } @@ -51,6 +65,7 @@ public: fTileModes[0] = params.fTileModes[0]; fTileModes[1] = params.fTileModes[1]; fFilterMode = params.fFilterMode; + fSRGBMode = params.fSRGBMode; return *this; } @@ -61,12 +76,20 @@ public: void reset(SkShader::TileMode tileXAndY, FilterMode filterMode) { fTileModes[0] = fTileModes[1] = tileXAndY; fFilterMode = filterMode; + fSRGBMode = kRespectDestination_SRGBMode; + } + + void reset(SkShader::TileMode tileXandY, FilterMode filterMode, SRGBMode srgbMode) { + fTileModes[0] = fTileModes[1] = tileXandY; + fFilterMode = filterMode; + fSRGBMode = srgbMode; } void reset(const SkShader::TileMode tileModes[2], FilterMode filterMode) { fTileModes[0] = tileModes[0]; fTileModes[1] = tileModes[1]; fFilterMode = filterMode; + fSRGBMode = kRespectDestination_SRGBMode; } void setClampNoFilter() { @@ -84,6 +107,8 @@ public: void setTileModeY(const SkShader::TileMode tm) { fTileModes[1] = tm; } void setTileModeXAndY(const SkShader::TileMode tm) { fTileModes[0] = fTileModes[1] = tm; } + void setSRGBMode(SRGBMode srgbMode) { fSRGBMode = srgbMode; } + SkShader::TileMode getTileModeX() const { return fTileModes[0]; } SkShader::TileMode getTileModeY() const { return fTileModes[1]; } @@ -95,10 +120,13 @@ public: FilterMode filterMode() const { return fFilterMode; } + SRGBMode srgbMode() const { return fSRGBMode; } + bool operator== (const GrTextureParams& other) const { return fTileModes[0] == other.fTileModes[0] && fTileModes[1] == other.fTileModes[1] && - fFilterMode == other.fFilterMode; + fFilterMode == other.fFilterMode && + fSRGBMode == other.fSRGBMode; } bool operator!= (const GrTextureParams& other) const { return !(*this == other); } @@ -106,5 +134,6 @@ public: private: SkShader::TileMode fTileModes[2]; FilterMode fFilterMode; + SRGBMode fSRGBMode; }; #endif diff --git a/gfx/skia/skia/include/gpu/GrTextureProvider.h b/gfx/skia/skia/include/gpu/GrTextureProvider.h index 338dddbaca..7c12ebd6db 100644 --- a/gfx/skia/skia/include/gpu/GrTextureProvider.h +++ b/gfx/skia/skia/include/gpu/GrTextureProvider.h @@ -9,6 +9,7 @@ #define GrTextureProvider_DEFINED #include "GrTexture.h" +#include "GrTypes.h" #include "SkImageFilter.h" class GrSingleOwner; @@ -22,19 +23,29 @@ public: * Creates a new texture in the resource cache and returns it. The caller owns a * ref on the returned texture which must be balanced by a call to unref. * - * @param desc Description of the texture properties. - * @param budgeted Does the texture count against the resource cache budget? + * @param desc Description of the texture properties. + * @param budgeted Does the texture count against the resource cache budget? + * @param texels A contiguous array of mipmap levels + * @param mipLevelCount The amount of elements in the texels array + */ + GrTexture* createMipMappedTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted, + const GrMipLevel* texels, int mipLevelCount); + + /** + * This function is a shim which creates a SkTArray of size 1. + * It then calls createTexture with that SkTArray. + * * @param srcData Pointer to the pixel values (optional). * @param rowBytes The number of bytes between rows of the texture. Zero * implies tightly packed rows. For compressed pixel configs, this * field is ignored. */ - GrTexture* createTexture(const GrSurfaceDesc& desc, bool budgeted, const void* srcData, + GrTexture* createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted, const void* srcData, size_t rowBytes); /** Shortcut for creating a texture with no initial data to upload. */ - GrTexture* createTexture(const GrSurfaceDesc& desc, bool budgeted) { - return this->createTexture(desc, budgeted, NULL, 0); + GrTexture* createTexture(const GrSurfaceDesc& desc, SkBudgeted budgeted) { + return this->createTexture(desc, budgeted, nullptr, 0); } /** Assigns a unique key to the texture. The texture will be findable via this key using @@ -73,7 +84,7 @@ public: if (kApprox_ScratchTexMatch == match) { return this->createApproxTexture(desc); } else { - return this->createTexture(desc, true); + return this->createTexture(desc, SkBudgeted::kYes); } } @@ -98,7 +109,7 @@ public: * the client will resolve to a texture). Currently wrapped render targets * always use the kBorrow_GrWrapOwnership semantics. * - * @return GrTexture object or NULL on failure. + * @return GrRenderTarget object or NULL on failure. */ GrRenderTarget* wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc); diff --git a/gfx/skia/skia/include/gpu/GrTypes.h b/gfx/skia/skia/include/gpu/GrTypes.h index dbcb9a6583..47c13ec02d 100644 --- a/gfx/skia/skia/include/gpu/GrTypes.h +++ b/gfx/skia/skia/include/gpu/GrTypes.h @@ -8,9 +8,9 @@ #ifndef GrTypes_DEFINED #define GrTypes_DEFINED +#include "SkMath.h" #include "SkTypes.h" #include "GrConfig.h" -#include "SkMath.h" //////////////////////////////////////////////////////////////////////////////// @@ -217,6 +217,10 @@ enum GrPixelConfig { * Premultiplied and sRGB. Byte order is r,g,b,a. */ kSRGBA_8888_GrPixelConfig, + /** + * Premultiplied and sRGB. Byte order is b,g,r,a. + */ + kSBGRA_8888_GrPixelConfig, /** * ETC1 Compressed Data */ @@ -268,8 +272,10 @@ static const int kGrPixelConfigCnt = kLast_GrPixelConfig + 1; #endif #if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) static const GrPixelConfig kSkia8888_GrPixelConfig = kBGRA_8888_GrPixelConfig; + static const GrPixelConfig kSkiaGamma8888_GrPixelConfig = kSBGRA_8888_GrPixelConfig; #elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) static const GrPixelConfig kSkia8888_GrPixelConfig = kRGBA_8888_GrPixelConfig; + static const GrPixelConfig kSkiaGamma8888_GrPixelConfig = kSRGBA_8888_GrPixelConfig; #else #error "SK_*32_SHIFT values must correspond to GL_BGRA or GL_RGBA format." #endif @@ -311,6 +317,7 @@ static inline bool GrPixelConfigIs8888(GrPixelConfig config) { case kRGBA_8888_GrPixelConfig: case kBGRA_8888_GrPixelConfig: case kSRGBA_8888_GrPixelConfig: + case kSBGRA_8888_GrPixelConfig: return true; default: return false; @@ -322,6 +329,7 @@ static inline bool GrPixelConfigIs8888(GrPixelConfig config) { static inline bool GrPixelConfigIsSRGB(GrPixelConfig config) { switch (config) { case kSRGBA_8888_GrPixelConfig: + case kSBGRA_8888_GrPixelConfig: return true; default: return false; @@ -336,6 +344,10 @@ static inline GrPixelConfig GrPixelConfigSwapRAndB(GrPixelConfig config) { return kRGBA_8888_GrPixelConfig; case kRGBA_8888_GrPixelConfig: return kBGRA_8888_GrPixelConfig; + case kSBGRA_8888_GrPixelConfig: + return kSRGBA_8888_GrPixelConfig; + case kSRGBA_8888_GrPixelConfig: + return kSBGRA_8888_GrPixelConfig; default: return kUnknown_GrPixelConfig; } @@ -353,6 +365,7 @@ static inline size_t GrBytesPerPixel(GrPixelConfig config) { case kRGBA_8888_GrPixelConfig: case kBGRA_8888_GrPixelConfig: case kSRGBA_8888_GrPixelConfig: + case kSBGRA_8888_GrPixelConfig: return 4; case kRGBA_half_GrPixelConfig: return 8; @@ -409,6 +422,9 @@ enum GrSurfaceFlags { GR_MAKE_BITFIELD_OPS(GrSurfaceFlags) +// opaque type for 3D API object handles +typedef intptr_t GrBackendObject; + /** * Some textures will be stored such that the upper and left edges of the content meet at the * the origin (in texture coord space) and for other textures the lower and left edges meet at @@ -422,6 +438,63 @@ enum GrSurfaceOrigin { kBottomLeft_GrSurfaceOrigin, }; +struct GrMipLevel { + const void* fPixels; + size_t fRowBytes; +}; + +/** + * An container of function pointers which consumers of Skia can fill in and + * pass to Skia. Skia will use these function pointers in place of its backend + * API texture creation function. Either all of the function pointers should be + * filled in, or they should all be nullptr. + */ +struct GrTextureStorageAllocator { + GrTextureStorageAllocator() + : fAllocateTextureStorage(nullptr) + , fDeallocateTextureStorage(nullptr) { + } + + enum class Result { + kSucceededAndUploaded, + kSucceededWithoutUpload, + kFailed + }; + typedef Result (*AllocateTextureStorageProc)( + void* ctx, GrBackendObject texture, unsigned width, + unsigned height, GrPixelConfig config, const void* srcData, GrSurfaceOrigin); + typedef void (*DeallocateTextureStorageProc)(void* ctx, GrBackendObject texture); + + /* + * Generates and binds a texture to |textureStorageTarget()|. Allocates + * storage for the texture. + * + * In OpenGL, the MIN and MAX filters for the created texture must be + * GL_LINEAR. The WRAP_S and WRAP_T must be GL_CLAMP_TO_EDGE. + * + * If |srcData| is not nullptr, then the implementation of this function + * may attempt to upload the data into the texture. On successful upload, + * or if |srcData| is nullptr, returns kSucceededAndUploaded. + */ + AllocateTextureStorageProc fAllocateTextureStorage; + + /* + * Deallocate the storage for the given texture. + * + * Skia does not always destroy its outstanding textures. See + * GrContext::abandonContext() for more details. The consumer of Skia is + * responsible for making sure that all textures are destroyed, even if this + * callback is not invoked. + */ + DeallocateTextureStorageProc fDeallocateTextureStorage; + + /* + * The context to use when invoking fAllocateTextureStorage and + * fDeallocateTextureStorage. + */ + void* fCtx; +}; + /** * Describes a surface to be created. */ @@ -432,7 +505,8 @@ struct GrSurfaceDesc { , fWidth(0) , fHeight(0) , fConfig(kUnknown_GrPixelConfig) - , fSampleCnt(0) { + , fSampleCnt(0) + , fIsMipMapped(false) { } GrSurfaceFlags fFlags; //!< bitfield of TextureFlags @@ -454,6 +528,14 @@ struct GrSurfaceDesc { * max supported count. */ int fSampleCnt; + + /** + * A custom platform-specific allocator to use in place of the backend APIs + * usual texture creation method (e.g. TexImage2D in OpenGL). + */ + GrTextureStorageAllocator fTextureStorageAllocator; + + bool fIsMipMapped; //!< Indicates if the texture has mipmaps }; // Legacy alias @@ -469,9 +551,6 @@ enum GrClipType { /////////////////////////////////////////////////////////////////////////////// -// opaque type for 3D API object handles -typedef intptr_t GrBackendObject; - /** Ownership rules for external GPU resources imported into Skia. */ enum GrWrapOwnership { diff --git a/gfx/skia/skia/include/gpu/GrTypesPriv.h b/gfx/skia/skia/include/gpu/GrTypesPriv.h index c46e25bd0d..39386f0140 100644 --- a/gfx/skia/skia/include/gpu/GrTypesPriv.h +++ b/gfx/skia/skia/include/gpu/GrTypesPriv.h @@ -9,7 +9,6 @@ #define GrTypesPriv_DEFINED #include "GrTypes.h" -#include "SkTArray.h" #include "SkRect.h" /** @@ -22,13 +21,17 @@ enum GrSLType { kVec2f_GrSLType, kVec3f_GrSLType, kVec4f_GrSLType, + kMat22f_GrSLType, kMat33f_GrSLType, kMat44f_GrSLType, kSampler2D_GrSLType, kSamplerExternal_GrSLType, kSampler2DRect_GrSLType, + kBool_GrSLType, + kInt_GrSLType, + kUint_GrSLType, - kLast_GrSLType = kSampler2DRect_GrSLType + kLast_GrSLType = kUint_GrSLType }; static const int kGrSLTypeCount = kLast_GrSLType + 1; @@ -41,6 +44,14 @@ enum GrShaderType { }; static const int kGrShaderTypeCount = kLastkFragment_GrShaderType + 1; +enum GrShaderFlags { + kNone_GrShaderFlags = 0, + kVertex_GrShaderFlag = 1 << kVertex_GrShaderType, + kGeometry_GrShaderFlag = 1 << kGeometry_GrShaderType, + kFragment_GrShaderFlag = 1 << kFragment_GrShaderType +}; +GR_MAKE_BITFIELD_OPS(GrShaderFlags); + /** * Precisions of shader language variables. Not all shading languages support precisions or actually * vary the internal precision based on the qualifiers. These currently only apply to float types ( @@ -65,7 +76,7 @@ static const int kGrSLPrecisionCount = kLast_GrSLPrecision + 1; */ static inline int GrSLTypeVectorCount(GrSLType type) { SkASSERT(type >= 0 && type < static_cast(kGrSLTypeCount)); - static const int kCounts[] = { -1, 1, 2, 3, 4, -1, -1, -1, -1, -1 }; + static const int kCounts[] = { -1, 1, 2, 3, 4, -1, -1, -1, -1, -1, -1, 1, 1, 1 }; return kCounts[type]; GR_STATIC_ASSERT(0 == kVoid_GrSLType); @@ -73,11 +84,15 @@ static inline int GrSLTypeVectorCount(GrSLType type) { GR_STATIC_ASSERT(2 == kVec2f_GrSLType); GR_STATIC_ASSERT(3 == kVec3f_GrSLType); GR_STATIC_ASSERT(4 == kVec4f_GrSLType); - GR_STATIC_ASSERT(5 == kMat33f_GrSLType); - GR_STATIC_ASSERT(6 == kMat44f_GrSLType); - GR_STATIC_ASSERT(7 == kSampler2D_GrSLType); - GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType); - GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType); + GR_STATIC_ASSERT(5 == kMat22f_GrSLType); + GR_STATIC_ASSERT(6 == kMat33f_GrSLType); + GR_STATIC_ASSERT(7 == kMat44f_GrSLType); + GR_STATIC_ASSERT(8 == kSampler2D_GrSLType); + GR_STATIC_ASSERT(9 == kSamplerExternal_GrSLType); + GR_STATIC_ASSERT(10 == kSampler2DRect_GrSLType); + GR_STATIC_ASSERT(11 == kBool_GrSLType); + GR_STATIC_ASSERT(12 == kInt_GrSLType); + GR_STATIC_ASSERT(13 == kUint_GrSLType); GR_STATIC_ASSERT(SK_ARRAY_COUNT(kCounts) == kGrSLTypeCount); } @@ -93,22 +108,53 @@ static inline GrSLType GrSLFloatVectorType(int count) { GR_STATIC_ASSERT(kVec4f_GrSLType == 4); } -/** Is the shading language type floating point (or vector/matrix of fp)? */ +/** Is the shading language type float (including vectors/matrices)? */ static inline bool GrSLTypeIsFloatType(GrSLType type) { SkASSERT(type >= 0 && type < static_cast(kGrSLTypeCount)); - return type >= 1 && type <= 6; + return type >= kFloat_GrSLType && type <= kMat44f_GrSLType; GR_STATIC_ASSERT(0 == kVoid_GrSLType); GR_STATIC_ASSERT(1 == kFloat_GrSLType); GR_STATIC_ASSERT(2 == kVec2f_GrSLType); GR_STATIC_ASSERT(3 == kVec3f_GrSLType); GR_STATIC_ASSERT(4 == kVec4f_GrSLType); - GR_STATIC_ASSERT(5 == kMat33f_GrSLType); - GR_STATIC_ASSERT(6 == kMat44f_GrSLType); - GR_STATIC_ASSERT(7 == kSampler2D_GrSLType); - GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType); - GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType); - GR_STATIC_ASSERT(10 == kGrSLTypeCount); + GR_STATIC_ASSERT(5 == kMat22f_GrSLType); + GR_STATIC_ASSERT(6 == kMat33f_GrSLType); + GR_STATIC_ASSERT(7 == kMat44f_GrSLType); + GR_STATIC_ASSERT(8 == kSampler2D_GrSLType); + GR_STATIC_ASSERT(9 == kSamplerExternal_GrSLType); + GR_STATIC_ASSERT(10 == kSampler2DRect_GrSLType); + GR_STATIC_ASSERT(11 == kBool_GrSLType); + GR_STATIC_ASSERT(12 == kInt_GrSLType); + GR_STATIC_ASSERT(13 == kUint_GrSLType); + GR_STATIC_ASSERT(14 == kGrSLTypeCount); +} + +/** Is the shading language type integral (including vectors/matrices)? */ +static inline bool GrSLTypeIsIntType(GrSLType type) { + SkASSERT(type >= 0 && type < static_cast(kGrSLTypeCount)); + return type >= kInt_GrSLType; + + GR_STATIC_ASSERT(0 == kVoid_GrSLType); + GR_STATIC_ASSERT(1 == kFloat_GrSLType); + GR_STATIC_ASSERT(2 == kVec2f_GrSLType); + GR_STATIC_ASSERT(3 == kVec3f_GrSLType); + GR_STATIC_ASSERT(4 == kVec4f_GrSLType); + GR_STATIC_ASSERT(5 == kMat22f_GrSLType); + GR_STATIC_ASSERT(6 == kMat33f_GrSLType); + GR_STATIC_ASSERT(7 == kMat44f_GrSLType); + GR_STATIC_ASSERT(8 == kSampler2D_GrSLType); + GR_STATIC_ASSERT(9 == kSamplerExternal_GrSLType); + GR_STATIC_ASSERT(10 == kSampler2DRect_GrSLType); + GR_STATIC_ASSERT(11 == kBool_GrSLType); + GR_STATIC_ASSERT(12 == kInt_GrSLType); + GR_STATIC_ASSERT(13 == kUint_GrSLType); + GR_STATIC_ASSERT(14 == kGrSLTypeCount); +} + +/** Is the shading language type numeric (including vectors/matrices)? */ +static inline bool GrSLTypeIsNumeric(GrSLType type) { + return GrSLTypeIsFloatType(type) || GrSLTypeIsIntType(type); } /** Returns the size in bytes for floating point GrSLTypes. For non floating point type returns 0 */ @@ -120,11 +166,15 @@ static inline size_t GrSLTypeSize(GrSLType type) { 2 * sizeof(float), // kVec2f_GrSLType 3 * sizeof(float), // kVec3f_GrSLType 4 * sizeof(float), // kVec4f_GrSLType - 9 * sizeof(float), // kMat33f_GrSLType - 16 * sizeof(float), // kMat44f_GrSLType + 2 * 2 * sizeof(float), // kMat22f_GrSLType + 3 * 3 * sizeof(float), // kMat33f_GrSLType + 4 * 4 * sizeof(float), // kMat44f_GrSLType 0, // kSampler2D_GrSLType - 0, // kSamplerExternal_GrSLType - 0 // kSampler2DRect_GrSLType + 0, // kSamplerExternal_GrSLType + 0, // kSampler2DRect_GrSLType + 0, // kBool_GrSLType + 0, // kInt_GrSLType + 0, // kUint_GrSLType }; return kSizes[type]; @@ -133,21 +183,29 @@ static inline size_t GrSLTypeSize(GrSLType type) { GR_STATIC_ASSERT(2 == kVec2f_GrSLType); GR_STATIC_ASSERT(3 == kVec3f_GrSLType); GR_STATIC_ASSERT(4 == kVec4f_GrSLType); - GR_STATIC_ASSERT(5 == kMat33f_GrSLType); - GR_STATIC_ASSERT(6 == kMat44f_GrSLType); - GR_STATIC_ASSERT(7 == kSampler2D_GrSLType); - GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType); - GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType); - GR_STATIC_ASSERT(10 == kGrSLTypeCount); + GR_STATIC_ASSERT(5 == kMat22f_GrSLType); + GR_STATIC_ASSERT(6 == kMat33f_GrSLType); + GR_STATIC_ASSERT(7 == kMat44f_GrSLType); + GR_STATIC_ASSERT(8 == kSampler2D_GrSLType); + GR_STATIC_ASSERT(9 == kSamplerExternal_GrSLType); + GR_STATIC_ASSERT(10 == kSampler2DRect_GrSLType); + GR_STATIC_ASSERT(11 == kBool_GrSLType); + GR_STATIC_ASSERT(12 == kInt_GrSLType); + GR_STATIC_ASSERT(13 == kUint_GrSLType); + GR_STATIC_ASSERT(14 == kGrSLTypeCount); } static inline bool GrSLTypeIsSamplerType(GrSLType type) { SkASSERT(type >= 0 && type < static_cast(kGrSLTypeCount)); - return type >= 7 && type <= 9; + return type >= kSampler2D_GrSLType && type <= kSampler2DRect_GrSLType; - GR_STATIC_ASSERT(7 == kSampler2D_GrSLType); - GR_STATIC_ASSERT(8 == kSamplerExternal_GrSLType); - GR_STATIC_ASSERT(9 == kSampler2DRect_GrSLType); + GR_STATIC_ASSERT(8 == kSampler2D_GrSLType); + GR_STATIC_ASSERT(9 == kSamplerExternal_GrSLType); + GR_STATIC_ASSERT(10 == kSampler2DRect_GrSLType); +} + +static inline bool GrSLTypeAcceptsPrecision(GrSLType type) { + return GrSLTypeIsNumeric(type) || GrSLTypeIsSamplerType(type); } ////////////////////////////////////////////////////////////////////////////// @@ -164,9 +222,12 @@ enum GrVertexAttribType { kUByte_GrVertexAttribType, // unsigned byte, e.g. coverage kVec4ub_GrVertexAttribType, // vector of 4 unsigned bytes, e.g. colors - kVec2s_GrVertexAttribType, // vector of 2 shorts, e.g. texture coordinates + kVec2us_GrVertexAttribType, // vector of 2 shorts, e.g. texture coordinates + + kInt_GrVertexAttribType, + kUint_GrVertexAttribType, - kLast_GrVertexAttribType = kVec2s_GrVertexAttribType + kLast_GrVertexAttribType = kUint_GrVertexAttribType }; static const int kGrVertexAttribTypeCount = kLast_GrVertexAttribType + 1; @@ -175,7 +236,7 @@ static const int kGrVertexAttribTypeCount = kLast_GrVertexAttribType + 1; */ static inline int GrVertexAttribTypeVectorCount(GrVertexAttribType type) { SkASSERT(type >= 0 && type < kGrVertexAttribTypeCount); - static const int kCounts[] = { 1, 2, 3, 4, 1, 4, 2 }; + static const int kCounts[] = { 1, 2, 3, 4, 1, 4, 2, 1, 1 }; return kCounts[type]; GR_STATIC_ASSERT(0 == kFloat_GrVertexAttribType); @@ -184,7 +245,9 @@ static inline int GrVertexAttribTypeVectorCount(GrVertexAttribType type) { GR_STATIC_ASSERT(3 == kVec4f_GrVertexAttribType); GR_STATIC_ASSERT(4 == kUByte_GrVertexAttribType); GR_STATIC_ASSERT(5 == kVec4ub_GrVertexAttribType); - GR_STATIC_ASSERT(6 == kVec2s_GrVertexAttribType); + GR_STATIC_ASSERT(6 == kVec2us_GrVertexAttribType); + GR_STATIC_ASSERT(7 == kInt_GrVertexAttribType); + GR_STATIC_ASSERT(8 == kUint_GrVertexAttribType); GR_STATIC_ASSERT(SK_ARRAY_COUNT(kCounts) == kGrVertexAttribTypeCount); } @@ -192,7 +255,6 @@ static inline int GrVertexAttribTypeVectorCount(GrVertexAttribType type) { * Returns the size of the attrib type in bytes. */ static inline size_t GrVertexAttribTypeSize(GrVertexAttribType type) { - SkASSERT(type >= 0 && type < kGrVertexAttribTypeCount); static const size_t kSizes[] = { sizeof(float), // kFloat_GrVertexAttribType 2*sizeof(float), // kVec2f_GrVertexAttribType @@ -200,7 +262,9 @@ static inline size_t GrVertexAttribTypeSize(GrVertexAttribType type) { 4*sizeof(float), // kVec4f_GrVertexAttribType 1*sizeof(char), // kUByte_GrVertexAttribType 4*sizeof(char), // kVec4ub_GrVertexAttribType - 2*sizeof(int16_t) // kVec2s_GrVertexAttribType + 2*sizeof(int16_t), // kVec2us_GrVertexAttribType + sizeof(int32_t), // kInt_GrVertexAttribType + sizeof(uint32_t) // kUint_GrVertexAttribType }; return kSizes[type]; @@ -210,10 +274,31 @@ static inline size_t GrVertexAttribTypeSize(GrVertexAttribType type) { GR_STATIC_ASSERT(3 == kVec4f_GrVertexAttribType); GR_STATIC_ASSERT(4 == kUByte_GrVertexAttribType); GR_STATIC_ASSERT(5 == kVec4ub_GrVertexAttribType); - GR_STATIC_ASSERT(6 == kVec2s_GrVertexAttribType); + GR_STATIC_ASSERT(6 == kVec2us_GrVertexAttribType); + GR_STATIC_ASSERT(7 == kInt_GrVertexAttribType); + GR_STATIC_ASSERT(8 == kUint_GrVertexAttribType); GR_STATIC_ASSERT(SK_ARRAY_COUNT(kSizes) == kGrVertexAttribTypeCount); } +/** + * Is the attrib type integral? + */ +static inline bool GrVertexAttribTypeIsIntType(GrVertexAttribType type) { + SkASSERT(type >= 0 && type < static_cast(kGrVertexAttribTypeCount)); + return type >= kInt_GrVertexAttribType; + + GR_STATIC_ASSERT(0 == kFloat_GrVertexAttribType); + GR_STATIC_ASSERT(1 == kVec2f_GrVertexAttribType); + GR_STATIC_ASSERT(2 == kVec3f_GrVertexAttribType); + GR_STATIC_ASSERT(3 == kVec4f_GrVertexAttribType); + GR_STATIC_ASSERT(4 == kUByte_GrVertexAttribType); + GR_STATIC_ASSERT(5 == kVec4ub_GrVertexAttribType); + GR_STATIC_ASSERT(6 == kVec2us_GrVertexAttribType); + GR_STATIC_ASSERT(7 == kInt_GrVertexAttribType); + GR_STATIC_ASSERT(8 == kUint_GrVertexAttribType); + GR_STATIC_ASSERT(9 == kGrVertexAttribTypeCount); +} + /** * converts a GrVertexAttribType to a GrSLType */ @@ -225,7 +310,7 @@ static inline GrSLType GrVertexAttribTypeToSLType(GrVertexAttribType type) { case kUByte_GrVertexAttribType: case kFloat_GrVertexAttribType: return kFloat_GrSLType; - case kVec2s_GrVertexAttribType: + case kVec2us_GrVertexAttribType: case kVec2f_GrVertexAttribType: return kVec2f_GrSLType; case kVec3f_GrVertexAttribType: @@ -233,6 +318,10 @@ static inline GrSLType GrVertexAttribTypeToSLType(GrVertexAttribType type) { case kVec4ub_GrVertexAttribType: case kVec4f_GrVertexAttribType: return kVec4f_GrSLType; + case kInt_GrVertexAttribType: + return kInt_GrSLType; + case kUint_GrVertexAttribType: + return kUint_GrSLType; } } @@ -312,13 +401,40 @@ private: }; /** - * Indicates the transfer direction for a transfer buffer - */ -enum TransferType { - /** Caller intends to use the buffer to transfer data to the GPU */ - kCpuToGpu_TransferType, - /** Caller intends to use the buffer to transfer data from the GPU */ - kGpuToCpu_TransferType +* Indicates the type of data that a GPU buffer will be used for. +*/ +enum GrBufferType { + kVertex_GrBufferType, + kIndex_GrBufferType, + kTexel_GrBufferType, + kDrawIndirect_GrBufferType, + kXferCpuToGpu_GrBufferType, + kXferGpuToCpu_GrBufferType, + + kLast_GrBufferType = kXferGpuToCpu_GrBufferType +}; +static const int kGrBufferTypeCount = kLast_GrBufferType + 1; + +static inline bool GrBufferTypeIsVertexOrIndex(GrBufferType type) { + SkASSERT(type >= 0 && type < kGrBufferTypeCount); + return type <= kIndex_GrBufferType; + + GR_STATIC_ASSERT(0 == kVertex_GrBufferType); + GR_STATIC_ASSERT(1 == kIndex_GrBufferType); +} + +/** +* Provides a performance hint regarding the frequency at which a data store will be accessed. +*/ +enum GrAccessPattern { + /** Data store will be respecified repeatedly and used many times. */ + kDynamic_GrAccessPattern, + /** Data store will be specified once and used many times. (Thus disqualified from caching.) */ + kStatic_GrAccessPattern, + /** Data store will be specified once and used at most a few times. (Also can't be cached.) */ + kStream_GrAccessPattern, + + kLast_GrAccessPattern = kStream_GrAccessPattern }; diff --git a/gfx/skia/skia/include/gpu/GrXferProcessor.h b/gfx/skia/skia/include/gpu/GrXferProcessor.h index ce26709d9a..3a698143c3 100644 --- a/gfx/skia/skia/include/gpu/GrXferProcessor.h +++ b/gfx/skia/skia/include/gpu/GrXferProcessor.h @@ -355,15 +355,19 @@ private: const GrPipelineOptimizations& optimizations, bool hasMixedSamples, const DstTexture*) const = 0; + + virtual bool onIsEqual(const GrXPFactory&) const = 0; + + bool willReadDstColor(const GrCaps& caps, + const GrPipelineOptimizations& optimizations, + bool hasMixedSamples) const; /** * Returns true if the XP generated by this factory will explicitly read dst in the fragment * shader. */ - virtual bool willReadDstColor(const GrCaps& caps, - const GrPipelineOptimizations& optimizations, - bool hasMixedSamples) const = 0; - - virtual bool onIsEqual(const GrXPFactory&) const = 0; + virtual bool onWillReadDstColor(const GrCaps& caps, + const GrPipelineOptimizations& optimizations, + bool hasMixedSamples) const = 0; static uint32_t GenClassID() { // fCurrXPFactoryID has been initialized to kIllegalXPFactoryID. The diff --git a/gfx/skia/skia/include/gpu/SkGr.h b/gfx/skia/skia/include/gpu/SkGr.h index 17625c4e4e..6a73636c69 100644 --- a/gfx/skia/skia/include/gpu/SkGr.h +++ b/gfx/skia/skia/include/gpu/SkGr.h @@ -1,4 +1,3 @@ - /* * Copyright 2010 Google Inc. * @@ -16,6 +15,7 @@ #include "SkFilterQuality.h" #include "SkImageInfo.h" +class GrCaps; class GrContext; class GrTexture; class GrTextureParams; @@ -71,10 +71,10 @@ static inline GrColor SkPMColorToGrColor(SkPMColor c) { GrTexture* GrRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams&); // TODO: Move SkImageInfo2GrPixelConfig to SkGrPriv.h (requires cleanup to SkWindow its subclasses). -GrPixelConfig SkImageInfo2GrPixelConfig(SkColorType, SkAlphaType, SkColorProfileType); +GrPixelConfig SkImageInfo2GrPixelConfig(SkColorType, SkAlphaType, SkColorProfileType, const GrCaps&); -static inline GrPixelConfig SkImageInfo2GrPixelConfig(const SkImageInfo& info) { - return SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType(), info.profileType()); +static inline GrPixelConfig SkImageInfo2GrPixelConfig(const SkImageInfo& info, const GrCaps& caps) { + return SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType(), info.profileType(), caps); } GrTextureParams::FilterMode GrSkFilterQualityToGrFilterMode(SkFilterQuality paintFilterQuality, diff --git a/gfx/skia/skia/include/gpu/effects/GrCoverageSetOpXP.h b/gfx/skia/skia/include/gpu/effects/GrCoverageSetOpXP.h index 097a890d56..42ac6ac528 100644 --- a/gfx/skia/skia/include/gpu/effects/GrCoverageSetOpXP.h +++ b/gfx/skia/skia/include/gpu/effects/GrCoverageSetOpXP.h @@ -34,9 +34,9 @@ private: bool hasMixedSamples, const DstTexture*) const override; - bool willReadDstColor(const GrCaps& /*caps*/, - const GrPipelineOptimizations& /*optimizations*/, - bool /*hasMixedSamples*/) const override { + bool onWillReadDstColor(const GrCaps& /*caps*/, + const GrPipelineOptimizations& /*optimizations*/, + bool /*hasMixedSamples*/) const override { return false; } diff --git a/gfx/skia/skia/include/gpu/effects/GrPorterDuffXferProcessor.h b/gfx/skia/skia/include/gpu/effects/GrPorterDuffXferProcessor.h index 457c6eabc8..865ef44253 100644 --- a/gfx/skia/skia/include/gpu/effects/GrPorterDuffXferProcessor.h +++ b/gfx/skia/skia/include/gpu/effects/GrPorterDuffXferProcessor.h @@ -60,9 +60,9 @@ private: bool hasMixedSamples, const DstTexture*) const override; - bool willReadDstColor(const GrCaps& caps, - const GrPipelineOptimizations& optimizations, - bool hasMixedSamples) const override; + bool onWillReadDstColor(const GrCaps& caps, + const GrPipelineOptimizations& optimizations, + bool hasMixedSamples) const override; bool onIsEqual(const GrXPFactory& xpfBase) const override { const GrPorterDuffXPFactory& xpf = xpfBase.cast(); diff --git a/gfx/skia/skia/src/gpu/gl/GrGLAssembleInterface.h b/gfx/skia/skia/include/gpu/gl/GrGLAssembleInterface.h similarity index 77% rename from gfx/skia/skia/src/gpu/gl/GrGLAssembleInterface.h rename to gfx/skia/skia/include/gpu/gl/GrGLAssembleInterface.h index 2a4835be38..b9881a99b4 100644 --- a/gfx/skia/skia/src/gpu/gl/GrGLAssembleInterface.h +++ b/gfx/skia/skia/include/gpu/gl/GrGLAssembleInterface.h @@ -1,4 +1,3 @@ - /* * Copyright 2014 Google Inc. * @@ -15,17 +14,17 @@ typedef GrGLFuncPtr (*GrGLGetProc)(void* ctx, const char name[]); * Generic function for creating a GrGLInterface for an either OpenGL or GLES. It calls * get() to get each function address. ctx is a generic ptr passed to and interpreted by get(). */ -const GrGLInterface* GrGLAssembleInterface(void* ctx, GrGLGetProc get); +SK_API const GrGLInterface* GrGLAssembleInterface(void* ctx, GrGLGetProc get); /** * Generic function for creating a GrGLInterface for an OpenGL (but not GLES) context. It calls * get() to get each function address. ctx is a generic ptr passed to and interpreted by get(). */ -const GrGLInterface* GrGLAssembleGLInterface(void* ctx, GrGLGetProc get); +SK_API const GrGLInterface* GrGLAssembleGLInterface(void* ctx, GrGLGetProc get); /** * Generic function for creating a GrGLInterface for an OpenGL ES (but not Open GL) context. It * calls get() to get each function address. ctx is a generic ptr passed to and interpreted by * get(). */ -const GrGLInterface* GrGLAssembleGLESInterface(void* ctx, GrGLGetProc get); +SK_API const GrGLInterface* GrGLAssembleGLESInterface(void* ctx, GrGLGetProc get); diff --git a/gfx/skia/skia/include/gpu/gl/GrGLConfig.h b/gfx/skia/skia/include/gpu/gl/GrGLConfig.h index c91537cc38..82963a7520 100644 --- a/gfx/skia/skia/include/gpu/gl/GrGLConfig.h +++ b/gfx/skia/skia/include/gpu/gl/GrGLConfig.h @@ -56,15 +56,6 @@ * Chrome's cmd buffer will create a new allocation and memset the whole thing * to zero (for security reasons). Defaults to 1 (enabled). * - * GR_GL_PER_GL_FUNC_CALLBACK: When set to 1 the GrGLInterface object provides - * a function pointer that is called just before every gl function. The ptr must - * be valid (i.e. there is no NULL check). However, by default the callback will - * be set to a function that does nothing. The signature of the function is: - * void function(const GrGLInterface*) - * It is not extern "C". - * The GrGLInterface field fCallback specifies the function ptr and there is an - * additional field fCallbackData of type intptr_t for client data. - * * GR_GL_CHECK_ALLOC_WITH_GET_ERROR: If set to 1 this will then glTexImage, * glBufferData, glRenderbufferStorage, etc will be checked for errors. This * amounts to ensuring the error is GL_NO_ERROR, calling the allocating @@ -115,10 +106,6 @@ #define GR_GL_USE_BUFFER_DATA_NULL_HINT 1 #endif -#if !defined(GR_GL_PER_GL_FUNC_CALLBACK) - #define GR_GL_PER_GL_FUNC_CALLBACK 0 -#endif - #if !defined(GR_GL_CHECK_ALLOC_WITH_GET_ERROR) #define GR_GL_CHECK_ALLOC_WITH_GET_ERROR 1 #endif diff --git a/gfx/skia/skia/include/gpu/gl/GrGLConfig_chrome.h b/gfx/skia/skia/include/gpu/gl/GrGLConfig_chrome.h index 041a46d8c4..838e0543e0 100644 --- a/gfx/skia/skia/include/gpu/gl/GrGLConfig_chrome.h +++ b/gfx/skia/skia/include/gpu/gl/GrGLConfig_chrome.h @@ -15,9 +15,6 @@ // with NULL. #define GR_GL_USE_BUFFER_DATA_NULL_HINT 0 -// chrome uses this to set the context on each GL call. -#define GR_GL_PER_GL_FUNC_CALLBACK 1 - // Check error is even more expensive in chrome (cmd buffer flush). The // compositor also doesn't check its allocations. #define GR_GL_CHECK_ALLOC_WITH_GET_ERROR 0 diff --git a/gfx/skia/skia/include/gpu/gl/GrGLExtensions.h b/gfx/skia/skia/include/gpu/gl/GrGLExtensions.h index d64a268a46..dd088de671 100644 --- a/gfx/skia/skia/include/gpu/gl/GrGLExtensions.h +++ b/gfx/skia/skia/include/gpu/gl/GrGLExtensions.h @@ -8,9 +8,9 @@ #ifndef GrGLExtensions_DEFINED #define GrGLExtensions_DEFINED +#include "../../private/SkTArray.h" #include "GrGLFunctions.h" #include "SkString.h" -#include "SkTArray.h" struct GrGLInterface; @@ -39,10 +39,10 @@ public: * NULL if on desktop GL with version 3.0 or higher. Otherwise it will fail. */ bool init(GrGLStandard standard, - GrGLGetStringProc getString, - GrGLGetStringiProc getStringi, - GrGLGetIntegervProc getIntegerv, - GrEGLQueryStringProc queryString = nullptr, + GrGLFunction getString, + GrGLFunction getStringi, + GrGLFunction getIntegerv, + GrGLFunction queryString = nullptr, GrEGLDisplay eglDisplay = nullptr); bool isInitialized() const { return fInitialized; } diff --git a/gfx/skia/skia/include/gpu/gl/GrGLFunctions.h b/gfx/skia/skia/include/gpu/gl/GrGLFunctions.h index 1086186df8..ea1ed65714 100644 --- a/gfx/skia/skia/include/gpu/gl/GrGLFunctions.h +++ b/gfx/skia/skia/include/gpu/gl/GrGLFunctions.h @@ -9,18 +9,12 @@ #ifndef GrGLFunctions_DEFINED #define GrGLFunctions_DEFINED +#include #include "GrGLTypes.h" +#include "../private/SkTLogic.h" extern "C" { -typedef void (GR_GL_FUNCTION_TYPE* GRGLDEBUGPROC)(GrGLenum source, - GrGLenum type, - GrGLuint id, - GrGLenum severity, - GrGLsizei length, - const GrGLchar* message, - const void* userParam); - /////////////////////////////////////////////////////////////////////////////// typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLActiveTextureProc)(GrGLenum texture); @@ -45,13 +39,12 @@ typedef GrGLenum (GR_GL_FUNCTION_TYPE* GrGLCheckFramebufferStatusProc)(GrGLenum typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearProc)(GrGLbitfield mask); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearColorProc)(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearStencilProc)(GrGLint s); -typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClientActiveTextureProc)(GrGLenum texture); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLColorMaskProc)(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompileShaderProc)(GrGLuint shader); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompressedTexImage2DProc)(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompressedTexSubImage2DProc)(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLsizei imageSize, const GrGLvoid* data); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCopyTexSubImage2DProc)(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height); -typedef GrGLuint (GR_GL_FUNCTION_TYPE* GrGLCreateProgramProc)(void); +typedef GrGLuint (GR_GL_FUNCTION_TYPE* GrGLCreateProgramProc)(); typedef GrGLuint (GR_GL_FUNCTION_TYPE* GrGLCreateShaderProc)(GrGLenum type); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCullFaceProc)(GrGLenum mode); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteBuffersProc)(GrGLsizei n, const GrGLuint* buffers); @@ -66,10 +59,13 @@ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDepthMaskProc)(GrGLboolean flag); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDisableProc)(GrGLenum cap); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDisableVertexAttribArrayProc)(GrGLuint index); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawArraysProc)(GrGLenum mode, GrGLint first, GrGLsizei count); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawArraysInstancedProc)(GrGLenum mode, GrGLint first, GrGLsizei count, GrGLsizei primcount); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawArraysIndirectProc)(GrGLenum mode, GrGLvoid* indirect); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawBufferProc)(GrGLenum mode); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawBuffersProc)(GrGLsizei n, const GrGLenum* bufs); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawElementsProc)(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices); -typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEGLImageTargetTexture2DProc)(GrGLenum target, GrGLeglImage image); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawElementsInstancedProc)(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid *indices, GrGLsizei primcount); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawElementsIndirectProc)(GrGLenum mode, GrGLenum type, GrGLvoid* indirect); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEnableProc)(GrGLenum cap); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEnableVertexAttribArrayProc)(GrGLuint index); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEndQueryProc)(GrGLenum target); @@ -91,6 +87,7 @@ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetBufferParameterivProc)(GrGLenum ta typedef GrGLenum (GR_GL_FUNCTION_TYPE* GrGLGetErrorProc)(); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetFramebufferAttachmentParameterivProc)(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetIntegervProc)(GrGLenum pname, GrGLint* params); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetMultisamplefvProc)(GrGLenum pname, GrGLuint index, GrGLfloat* val); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetProgramInfoLogProc)(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, char* infolog); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetProgramivProc)(GrGLuint program, GrGLenum pname, GrGLint* params); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetQueryivProc)(GrGLenum GLtarget, GrGLenum pname, GrGLint *params); @@ -145,6 +142,7 @@ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilMaskProc)(GrGLuint mask); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilMaskSeparateProc)(GrGLenum face, GrGLuint mask); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilOpProc)(GrGLenum fail, GrGLenum zfail, GrGLenum zpass); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilOpSeparateProc)(GrGLenum face, GrGLenum fail, GrGLenum zfail, GrGLenum zpass); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexBufferProc)(GrGLenum target, GrGLenum internalformat, GrGLuint buffer); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexImage2DProc)(GrGLenum target, GrGLint level, GrGLint internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid* pixels); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexParameteriProc)(GrGLenum target, GrGLenum pname, GrGLint param); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexParameterivProc)(GrGLenum target, GrGLenum pname, const GrGLint* params); @@ -179,6 +177,8 @@ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttrib1fProc)(GrGLuint indx, co typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttrib2fvProc)(GrGLuint indx, const GrGLfloat* values); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttrib3fvProc)(GrGLuint indx, const GrGLfloat* values); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttrib4fvProc)(GrGLuint indx, const GrGLfloat* values); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttribDivisorProc)(GrGLuint index, GrGLuint divisor); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttribIPointerProc)(GrGLuint indx, GrGLint size, GrGLenum type, GrGLsizei stride, const GrGLvoid* ptr); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttribPointerProc)(GrGLuint indx, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const GrGLvoid* ptr); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLViewportProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height); @@ -216,12 +216,9 @@ typedef GrGLint (GR_GL_FUNCTION_TYPE* GrGLGetProgramResourceLocationProc)(GrGLui /* GL_NV_framebuffer_mixed_samples */ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCoverageModulationProc)(GrGLenum components); -/* ARB_draw_instanced */ -typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawArraysInstancedProc)(GrGLenum mode, GrGLint first, GrGLsizei count, GrGLsizei primcount); -typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawElementsInstancedProc)(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid *indices, GrGLsizei primcount); - -/* ARB_instanced_arrays */ -typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttribDivisorProc)(GrGLuint index, GrGLuint divisor); +/* EXT_multi_draw_indirect */ +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLMultiDrawArraysIndirectProc)(GrGLenum mode, const GrGLvoid *indirect, GrGLsizei drawcount, GrGLsizei stride); +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLMultiDrawElementsIndirectProc)(GrGLenum mode, GrGLenum type, const GrGLvoid *indirect, GrGLsizei drawcount, GrGLsizei stride); /* NV_bindless_texture */ typedef GrGLuint64 (GR_GL_FUNCTION_TYPE* GrGLGetTextureHandleProc)(GrGLuint texture); @@ -238,6 +235,9 @@ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniformHandleui64vProc)(GrGLint locat typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLProgramUniformHandleui64Proc)(GrGLuint program, GrGLint location, GrGLuint64 v0); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLProgramUniformHandleui64vProc)(GrGLuint program, GrGLint location, GrGLsizei count, const GrGLuint64 *value); +/* ARB_sample_shading */ +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLMinSampleShadingProc)(GrGLfloat value); + /* EXT_direct_state_access */ // (In the future some of these methods may be omitted) typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTextureParameteriProc)(GrGLuint texture, GrGLenum target, GrGLenum pname, GrGLint param); @@ -340,6 +340,8 @@ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetVertexArrayIntegeri_vProc)(GrGLuin typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetVertexArrayPointeri_vProc)(GrGLuint vaobj, GrGLuint index, GrGLenum pname, GrGLvoid **param); typedef GrGLvoid* (GR_GL_FUNCTION_TYPE* GrGLMapNamedBufferRangeProc)(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length, GrGLbitfield access); typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLFlushMappedNamedBufferRangeProc)(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length); +// OpenGL 3.1 +typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTextureBufferProc)(GrGLuint texture, GrGLenum target, GrGLenum internalformat, GrGLuint buffer); /* KHR_debug */ typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDebugMessageControlProc)(GrGLenum source, GrGLenum type, GrGLenum severity, GrGLsizei count, const GrGLuint* ids, GrGLboolean enabled); @@ -357,4 +359,6 @@ typedef GrEGLImage (GR_GL_FUNCTION_TYPE* GrEGLCreateImageProc)(GrEGLDisplay dpy, typedef GrEGLBoolean (GR_GL_FUNCTION_TYPE* GrEGLDestroyImageProc)(GrEGLDisplay dpy, GrEGLImage image); } // extern "C" +template using GrGLFunction = std::function>; + #endif diff --git a/gfx/skia/skia/include/gpu/gl/GrGLInterface.h b/gfx/skia/skia/include/gpu/gl/GrGLInterface.h index 31429a8ebc..4ba516617f 100644 --- a/gfx/skia/skia/include/gpu/gl/GrGLInterface.h +++ b/gfx/skia/skia/include/gpu/gl/GrGLInterface.h @@ -43,44 +43,18 @@ const GrGLInterface* GrGLDefaultInterface(); */ SK_API const GrGLInterface* GrGLCreateNativeInterface(); -#if SK_MESA -/** - * Creates a GrGLInterface for an OSMesa context. - */ -SK_API const GrGLInterface* GrGLCreateMesaInterface(); -#endif - -#if SK_ANGLE -/** - * Creates a GrGLInterface for an ANGLE context. - */ -SK_API const GrGLInterface* GrGLCreateANGLEInterface(); -#endif - -#if SK_COMMAND_BUFFER -/** - * Creates a GrGLInterface for a Command Buffer context. - */ -SK_API const GrGLInterface* GrGLCreateCommandBufferInterface(); -#endif - -/** - * Creates a null GrGLInterface that doesn't draw anything. Used for measuring - * CPU overhead. - */ -const SK_API GrGLInterface* GrGLCreateNullInterface(); - -/** - * Creates a debugging GrGLInterface that doesn't draw anything. Used for - * finding memory leaks and invalid memory accesses. - */ -const GrGLInterface* GrGLCreateDebugInterface(); - #if GR_GL_PER_GL_FUNC_CALLBACK typedef void (*GrGLInterfaceCallbackProc)(const GrGLInterface*); typedef intptr_t GrGLInterfaceCallbackData; #endif +/** + * Creates a null GrGLInterface that doesn't draw anything. Used for measuring + * CPU overhead. TODO: We would like to move this to tools/gpu/gl/null but currently + * Chromium is using it in its unit tests. + */ +const SK_API GrGLInterface* GrGLCreateNullInterface(); + /** Function that returns a new interface identical to "interface" but without support for GL_NV_path_rendering. */ const GrGLInterface* GrGLInterfaceRemoveNVPR(const GrGLInterface*); @@ -105,26 +79,6 @@ const GrGLInterface* GrGLInterfaceAddTestDebugMarker(const GrGLInterface*, */ struct SK_API GrGLInterface : public SkRefCnt { private: - // simple wrapper class that exists only to initialize a pointer to NULL - template class GLPtr { - public: - GLPtr() : fPtr(NULL) {} - GLPtr operator=(FNPTR_TYPE ptr) { fPtr = ptr; return *this; } - operator FNPTR_TYPE() const { return fPtr; } - private: - FNPTR_TYPE fPtr; - }; - - // This is a temporary workaround to keep Chromium's GrGLInterface factories compiling until - // they're updated to use the Functions struct. - template class GLPtrAlias { - public: - GLPtrAlias(GLPtr* base) : fBase(base) {} - void operator=(FNPTR_TYPE ptr) { *fBase = ptr; } - private: - GLPtr* fBase; - }; - typedef SkRefCnt INHERITED; public: @@ -152,109 +106,114 @@ public: * operator. */ struct Functions { - GLPtr fActiveTexture; - GLPtr fAttachShader; - GLPtr fBeginQuery; - GLPtr fBindAttribLocation; - GLPtr fBindBuffer; - GLPtr fBindFragDataLocation; - GLPtr fBindFragDataLocationIndexed; - GLPtr fBindFramebuffer; - GLPtr fBindRenderbuffer; - GLPtr fBindTexture; - GLPtr fBindVertexArray; - GLPtr fBlendBarrier; - GLPtr fBlendColor; - GLPtr fBlendEquation; - GLPtr fBlendFunc; - GLPtr fBlitFramebuffer; - GLPtr fBufferData; - GLPtr fBufferSubData; - GLPtr fCheckFramebufferStatus; - GLPtr fClear; - GLPtr fClearColor; - GLPtr fClearStencil; - GLPtr fColorMask; - GLPtr fCompileShader; - GLPtr fCompressedTexImage2D; - GLPtr fCompressedTexSubImage2D; - GLPtr fCopyTexSubImage2D; - GLPtr fCreateProgram; - GLPtr fCreateShader; - GLPtr fCullFace; - GLPtr fDeleteBuffers; - GLPtr fDeleteFramebuffers; - GLPtr fDeleteProgram; - GLPtr fDeleteQueries; - GLPtr fDeleteRenderbuffers; - GLPtr fDeleteShader; - GLPtr fDeleteTextures; - GLPtr fDeleteVertexArrays; - GLPtr fDepthMask; - GLPtr fDisable; - GLPtr fDisableVertexAttribArray; - GLPtr fDrawArrays; - GLPtr fDrawBuffer; - GLPtr fDrawBuffers; - GLPtr fDrawElements; - GLPtr fEnable; - GLPtr fEnableVertexAttribArray; - GLPtr fEndQuery; - GLPtr fFinish; - GLPtr fFlush; - GLPtr fFlushMappedBufferRange; - GLPtr fFramebufferRenderbuffer; - GLPtr fFramebufferTexture2D; - GLPtr fFramebufferTexture2DMultisample; - GLPtr fFrontFace; - GLPtr fGenBuffers; - GLPtr fGenFramebuffers; - GLPtr fGenerateMipmap; - GLPtr fGenQueries; - GLPtr fGenRenderbuffers; - GLPtr fGenTextures; - GLPtr fGenVertexArrays; - GLPtr fGetBufferParameteriv; - GLPtr fGetError; - GLPtr fGetFramebufferAttachmentParameteriv; - GLPtr fGetIntegerv; - GLPtr fGetQueryObjecti64v; - GLPtr fGetQueryObjectiv; - GLPtr fGetQueryObjectui64v; - GLPtr fGetQueryObjectuiv; - GLPtr fGetQueryiv; - GLPtr fGetProgramInfoLog; - GLPtr fGetProgramiv; - GLPtr fGetRenderbufferParameteriv; - GLPtr fGetShaderInfoLog; - GLPtr fGetShaderiv; - GLPtr fGetShaderPrecisionFormat; - GLPtr fGetString; - GLPtr fGetStringi; - GLPtr fGetTexLevelParameteriv; - GLPtr fGetUniformLocation; - GLPtr fInsertEventMarker; - GLPtr fInvalidateBufferData; - GLPtr fInvalidateBufferSubData; - GLPtr fInvalidateFramebuffer; - GLPtr fInvalidateSubFramebuffer; - GLPtr fInvalidateTexImage; - GLPtr fInvalidateTexSubImage; - GLPtr fIsTexture; - GLPtr fLineWidth; - GLPtr fLinkProgram; - GLPtr fMapBuffer; - GLPtr fMapBufferRange; - GLPtr fMapBufferSubData; - GLPtr fMapTexSubImage2D; - GLPtr fPixelStorei; - GLPtr fPopGroupMarker; - GLPtr fPushGroupMarker; - GLPtr fQueryCounter; - GLPtr fRasterSamples; - GLPtr fReadBuffer; - GLPtr fReadPixels; - GLPtr fRenderbufferStorage; + GrGLFunction fActiveTexture; + GrGLFunction fAttachShader; + GrGLFunction fBeginQuery; + GrGLFunction fBindAttribLocation; + GrGLFunction fBindBuffer; + GrGLFunction fBindFragDataLocation; + GrGLFunction fBindFragDataLocationIndexed; + GrGLFunction fBindFramebuffer; + GrGLFunction fBindRenderbuffer; + GrGLFunction fBindTexture; + GrGLFunction fBindVertexArray; + GrGLFunction fBlendBarrier; + GrGLFunction fBlendColor; + GrGLFunction fBlendEquation; + GrGLFunction fBlendFunc; + GrGLFunction fBlitFramebuffer; + GrGLFunction fBufferData; + GrGLFunction fBufferSubData; + GrGLFunction fCheckFramebufferStatus; + GrGLFunction fClear; + GrGLFunction fClearColor; + GrGLFunction fClearStencil; + GrGLFunction fColorMask; + GrGLFunction fCompileShader; + GrGLFunction fCompressedTexImage2D; + GrGLFunction fCompressedTexSubImage2D; + GrGLFunction fCopyTexSubImage2D; + GrGLFunction fCreateProgram; + GrGLFunction fCreateShader; + GrGLFunction fCullFace; + GrGLFunction fDeleteBuffers; + GrGLFunction fDeleteFramebuffers; + GrGLFunction fDeleteProgram; + GrGLFunction fDeleteQueries; + GrGLFunction fDeleteRenderbuffers; + GrGLFunction fDeleteShader; + GrGLFunction fDeleteTextures; + GrGLFunction fDeleteVertexArrays; + GrGLFunction fDepthMask; + GrGLFunction fDisable; + GrGLFunction fDisableVertexAttribArray; + GrGLFunction fDrawArrays; + GrGLFunction fDrawArraysIndirect; + GrGLFunction fDrawArraysInstanced; + GrGLFunction fDrawBuffer; + GrGLFunction fDrawBuffers; + GrGLFunction fDrawElements; + GrGLFunction fDrawElementsIndirect; + GrGLFunction fDrawElementsInstanced; + GrGLFunction fEnable; + GrGLFunction fEnableVertexAttribArray; + GrGLFunction fEndQuery; + GrGLFunction fFinish; + GrGLFunction fFlush; + GrGLFunction fFlushMappedBufferRange; + GrGLFunction fFramebufferRenderbuffer; + GrGLFunction fFramebufferTexture2D; + GrGLFunction fFramebufferTexture2DMultisample; + GrGLFunction fFrontFace; + GrGLFunction fGenBuffers; + GrGLFunction fGenFramebuffers; + GrGLFunction fGenerateMipmap; + GrGLFunction fGenQueries; + GrGLFunction fGenRenderbuffers; + GrGLFunction fGenTextures; + GrGLFunction fGenVertexArrays; + GrGLFunction fGetBufferParameteriv; + GrGLFunction fGetError; + GrGLFunction fGetFramebufferAttachmentParameteriv; + GrGLFunction fGetIntegerv; + GrGLFunction fGetMultisamplefv; + GrGLFunction fGetQueryObjecti64v; + GrGLFunction fGetQueryObjectiv; + GrGLFunction fGetQueryObjectui64v; + GrGLFunction fGetQueryObjectuiv; + GrGLFunction fGetQueryiv; + GrGLFunction fGetProgramInfoLog; + GrGLFunction fGetProgramiv; + GrGLFunction fGetRenderbufferParameteriv; + GrGLFunction fGetShaderInfoLog; + GrGLFunction fGetShaderiv; + GrGLFunction fGetShaderPrecisionFormat; + GrGLFunction fGetString; + GrGLFunction fGetStringi; + GrGLFunction fGetTexLevelParameteriv; + GrGLFunction fGetUniformLocation; + GrGLFunction fInsertEventMarker; + GrGLFunction fInvalidateBufferData; + GrGLFunction fInvalidateBufferSubData; + GrGLFunction fInvalidateFramebuffer; + GrGLFunction fInvalidateSubFramebuffer; + GrGLFunction fInvalidateTexImage; + GrGLFunction fInvalidateTexSubImage; + GrGLFunction fIsTexture; + GrGLFunction fLineWidth; + GrGLFunction fLinkProgram; + GrGLFunction fMapBuffer; + GrGLFunction fMapBufferRange; + GrGLFunction fMapBufferSubData; + GrGLFunction fMapTexSubImage2D; + GrGLFunction fPixelStorei; + GrGLFunction fPopGroupMarker; + GrGLFunction fPushGroupMarker; + GrGLFunction fQueryCounter; + GrGLFunction fRasterSamples; + GrGLFunction fReadBuffer; + GrGLFunction fReadPixels; + GrGLFunction fRenderbufferStorage; // On OpenGL ES there are multiple incompatible extensions that add support for MSAA // and ES3 adds MSAA support to the standard. On an ES3 driver we may still use the @@ -271,243 +230,242 @@ public: // functionality. // GL_EXT_multisampled_render_to_texture (preferred) or GL_IMG_multisampled_render_to_texture - GLPtr fRenderbufferStorageMultisampleES2EXT; + GrGLFunction fRenderbufferStorageMultisampleES2EXT; // GL_APPLE_framebuffer_multisample - GLPtr fRenderbufferStorageMultisampleES2APPLE; + GrGLFunction fRenderbufferStorageMultisampleES2APPLE; // This is used to store the pointer for GL_ARB/EXT/ANGLE/CHROMIUM_framebuffer_multisample or // the standard function in ES3+ or GL 3.0+. - GLPtr fRenderbufferStorageMultisample; + GrGLFunction fRenderbufferStorageMultisample; // Pointer to BindUniformLocationCHROMIUM from the GL_CHROMIUM_bind_uniform_location extension. - GLPtr fBindUniformLocation; + GrGLFunction fBindUniformLocation; - GLPtr fResolveMultisampleFramebuffer; - GLPtr fScissor; - GLPtr fShaderSource; - GLPtr fStencilFunc; - GLPtr fStencilFuncSeparate; - GLPtr fStencilMask; - GLPtr fStencilMaskSeparate; - GLPtr fStencilOp; - GLPtr fStencilOpSeparate; - GLPtr fTexImage2D; - GLPtr fTexParameteri; - GLPtr fTexParameteriv; - GLPtr fTexSubImage2D; - GLPtr fTexStorage2D; - GLPtr fTextureBarrier; - GLPtr fDiscardFramebuffer; - GLPtr fUniform1f; - GLPtr fUniform1i; - GLPtr fUniform1fv; - GLPtr fUniform1iv; - GLPtr fUniform2f; - GLPtr fUniform2i; - GLPtr fUniform2fv; - GLPtr fUniform2iv; - GLPtr fUniform3f; - GLPtr fUniform3i; - GLPtr fUniform3fv; - GLPtr fUniform3iv; - GLPtr fUniform4f; - GLPtr fUniform4i; - GLPtr fUniform4fv; - GLPtr fUniform4iv; - GLPtr fUniformMatrix2fv; - GLPtr fUniformMatrix3fv; - GLPtr fUniformMatrix4fv; - GLPtr fUnmapBuffer; - GLPtr fUnmapBufferSubData; - GLPtr fUnmapTexSubImage2D; - GLPtr fUseProgram; - GLPtr fVertexAttrib1f; - GLPtr fVertexAttrib2fv; - GLPtr fVertexAttrib3fv; - GLPtr fVertexAttrib4fv; - GLPtr fVertexAttribPointer; - GLPtr fViewport; + GrGLFunction fResolveMultisampleFramebuffer; + GrGLFunction fScissor; + GrGLFunction fShaderSource; + GrGLFunction fStencilFunc; + GrGLFunction fStencilFuncSeparate; + GrGLFunction fStencilMask; + GrGLFunction fStencilMaskSeparate; + GrGLFunction fStencilOp; + GrGLFunction fStencilOpSeparate; + GrGLFunction fTexBuffer; + GrGLFunction fTexImage2D; + GrGLFunction fTexParameteri; + GrGLFunction fTexParameteriv; + GrGLFunction fTexSubImage2D; + GrGLFunction fTexStorage2D; + GrGLFunction fTextureBarrier; + GrGLFunction fDiscardFramebuffer; + GrGLFunction fUniform1f; + GrGLFunction fUniform1i; + GrGLFunction fUniform1fv; + GrGLFunction fUniform1iv; + GrGLFunction fUniform2f; + GrGLFunction fUniform2i; + GrGLFunction fUniform2fv; + GrGLFunction fUniform2iv; + GrGLFunction fUniform3f; + GrGLFunction fUniform3i; + GrGLFunction fUniform3fv; + GrGLFunction fUniform3iv; + GrGLFunction fUniform4f; + GrGLFunction fUniform4i; + GrGLFunction fUniform4fv; + GrGLFunction fUniform4iv; + GrGLFunction fUniformMatrix2fv; + GrGLFunction fUniformMatrix3fv; + GrGLFunction fUniformMatrix4fv; + GrGLFunction fUnmapBuffer; + GrGLFunction fUnmapBufferSubData; + GrGLFunction fUnmapTexSubImage2D; + GrGLFunction fUseProgram; + GrGLFunction fVertexAttrib1f; + GrGLFunction fVertexAttrib2fv; + GrGLFunction fVertexAttrib3fv; + GrGLFunction fVertexAttrib4fv; + GrGLFunction fVertexAttribDivisor; + GrGLFunction fVertexAttribIPointer; + GrGLFunction fVertexAttribPointer; + GrGLFunction fViewport; /* GL_NV_path_rendering */ - GLPtr fMatrixLoadf; - GLPtr fMatrixLoadIdentity; - GLPtr fGetProgramResourceLocation; - GLPtr fPathCommands; - GLPtr fPathParameteri; - GLPtr fPathParameterf; - GLPtr fGenPaths; - GLPtr fDeletePaths; - GLPtr fIsPath; - GLPtr fPathStencilFunc; - GLPtr fStencilFillPath; - GLPtr fStencilStrokePath; - GLPtr fStencilFillPathInstanced; - GLPtr fStencilStrokePathInstanced; - GLPtr fCoverFillPath; - GLPtr fCoverStrokePath; - GLPtr fCoverFillPathInstanced; - GLPtr fCoverStrokePathInstanced; + GrGLFunction fMatrixLoadf; + GrGLFunction fMatrixLoadIdentity; + GrGLFunction fGetProgramResourceLocation; + GrGLFunction fPathCommands; + GrGLFunction fPathParameteri; + GrGLFunction fPathParameterf; + GrGLFunction fGenPaths; + GrGLFunction fDeletePaths; + GrGLFunction fIsPath; + GrGLFunction fPathStencilFunc; + GrGLFunction fStencilFillPath; + GrGLFunction fStencilStrokePath; + GrGLFunction fStencilFillPathInstanced; + GrGLFunction fStencilStrokePathInstanced; + GrGLFunction fCoverFillPath; + GrGLFunction fCoverStrokePath; + GrGLFunction fCoverFillPathInstanced; + GrGLFunction fCoverStrokePathInstanced; // NV_path_rendering v1.2 - GLPtr fStencilThenCoverFillPath; - GLPtr fStencilThenCoverStrokePath; - GLPtr fStencilThenCoverFillPathInstanced; - GLPtr fStencilThenCoverStrokePathInstanced; + GrGLFunction fStencilThenCoverFillPath; + GrGLFunction fStencilThenCoverStrokePath; + GrGLFunction fStencilThenCoverFillPathInstanced; + GrGLFunction fStencilThenCoverStrokePathInstanced; // NV_path_rendering v1.3 - GLPtr fProgramPathFragmentInputGen; + GrGLFunction fProgramPathFragmentInputGen; // CHROMIUM_path_rendering - GLPtr fBindFragmentInputLocation; + GrGLFunction fBindFragmentInputLocation; /* NV_framebuffer_mixed_samples */ - GLPtr fCoverageModulation; + GrGLFunction fCoverageModulation; - /* ARB_draw_instanced */ - GLPtr fDrawArraysInstanced; - GLPtr fDrawElementsInstanced; - - /* ARB_instanced_arrays */ - GLPtr fVertexAttribDivisor; + /* EXT_multi_draw_indirect */ + GrGLFunction fMultiDrawArraysIndirect; + GrGLFunction fMultiDrawElementsIndirect; /* NV_bindless_texture */ // We use the NVIDIA verson for now because it does not require dynamically uniform handles. // We may switch the the ARB version and/or omit methods in the future. - GLPtr fGetTextureHandle; - GLPtr fGetTextureSamplerHandle; - GLPtr fMakeTextureHandleResident; - GLPtr fMakeTextureHandleNonResident; - GLPtr fGetImageHandle; - GLPtr fMakeImageHandleResident; - GLPtr fMakeImageHandleNonResident; - GLPtr fIsTextureHandleResident; - GLPtr fIsImageHandleResident; - GLPtr fUniformHandleui64; - GLPtr fUniformHandleui64v; - GLPtr fProgramUniformHandleui64; - GLPtr fProgramUniformHandleui64v; + GrGLFunction fGetTextureHandle; + GrGLFunction fGetTextureSamplerHandle; + GrGLFunction fMakeTextureHandleResident; + GrGLFunction fMakeTextureHandleNonResident; + GrGLFunction fGetImageHandle; + GrGLFunction fMakeImageHandleResident; + GrGLFunction fMakeImageHandleNonResident; + GrGLFunction fIsTextureHandleResident; + GrGLFunction fIsImageHandleResident; + GrGLFunction fUniformHandleui64; + GrGLFunction fUniformHandleui64v; + GrGLFunction fProgramUniformHandleui64; + GrGLFunction fProgramUniformHandleui64v; + + /* ARB_sample_shading */ + GrGLFunction fMinSampleShading; /* EXT_direct_state_access */ // We use the EXT verson because it is more expansive and interacts with more extensions // than the ARB or core (4.5) versions. We may switch and/or omit methods in the future. - GLPtr fTextureParameteri; - GLPtr fTextureParameteriv; - GLPtr fTextureParameterf; - GLPtr fTextureParameterfv; - GLPtr fTextureImage1D; - GLPtr fTextureImage2D; - GLPtr fTextureSubImage1D; - GLPtr fTextureSubImage2D; - GLPtr fCopyTextureImage1D; - GLPtr fCopyTextureImage2D; - GLPtr fCopyTextureSubImage1D; - GLPtr fCopyTextureSubImage2D; - GLPtr fGetTextureImage; - GLPtr fGetTextureParameterfv; - GLPtr fGetTextureParameteriv; - GLPtr fGetTextureLevelParameterfv; - GLPtr fGetTextureLevelParameteriv; + GrGLFunction fTextureParameteri; + GrGLFunction fTextureParameteriv; + GrGLFunction fTextureParameterf; + GrGLFunction fTextureParameterfv; + GrGLFunction fTextureImage1D; + GrGLFunction fTextureImage2D; + GrGLFunction fTextureSubImage1D; + GrGLFunction fTextureSubImage2D; + GrGLFunction fCopyTextureImage1D; + GrGLFunction fCopyTextureImage2D; + GrGLFunction fCopyTextureSubImage1D; + GrGLFunction fCopyTextureSubImage2D; + GrGLFunction fGetTextureImage; + GrGLFunction fGetTextureParameterfv; + GrGLFunction fGetTextureParameteriv; + GrGLFunction fGetTextureLevelParameterfv; + GrGLFunction fGetTextureLevelParameteriv; // OpenGL 1.2 - GLPtr fTextureImage3D; - GLPtr fTextureSubImage3D; - GLPtr fCopyTextureSubImage3D; - GLPtr fCompressedTextureImage3D; - GLPtr fCompressedTextureImage2D; - GLPtr fCompressedTextureImage1D; - GLPtr fCompressedTextureSubImage3D; - GLPtr fCompressedTextureSubImage2D; - GLPtr fCompressedTextureSubImage1D; - GLPtr fGetCompressedTextureImage; + GrGLFunction fTextureImage3D; + GrGLFunction fTextureSubImage3D; + GrGLFunction fCopyTextureSubImage3D; + GrGLFunction fCompressedTextureImage3D; + GrGLFunction fCompressedTextureImage2D; + GrGLFunction fCompressedTextureImage1D; + GrGLFunction fCompressedTextureSubImage3D; + GrGLFunction fCompressedTextureSubImage2D; + GrGLFunction fCompressedTextureSubImage1D; + GrGLFunction fGetCompressedTextureImage; // OpenGL 1.5 - GLPtr fNamedBufferData; - GLPtr fNamedBufferSubData; - GLPtr fMapNamedBuffer; - GLPtr fUnmapNamedBuffer; - GLPtr fGetNamedBufferParameteriv; - GLPtr fGetNamedBufferPointerv; - GLPtr fGetNamedBufferSubData; + GrGLFunction fNamedBufferData; + GrGLFunction fNamedBufferSubData; + GrGLFunction fMapNamedBuffer; + GrGLFunction fUnmapNamedBuffer; + GrGLFunction fGetNamedBufferParameteriv; + GrGLFunction fGetNamedBufferPointerv; + GrGLFunction fGetNamedBufferSubData; // OpenGL 2.0 - GLPtr fProgramUniform1f; - GLPtr fProgramUniform2f; - GLPtr fProgramUniform3f; - GLPtr fProgramUniform4f; - GLPtr fProgramUniform1i; - GLPtr fProgramUniform2i; - GLPtr fProgramUniform3i; - GLPtr fProgramUniform4i; - GLPtr fProgramUniform1fv; - GLPtr fProgramUniform2fv; - GLPtr fProgramUniform3fv; - GLPtr fProgramUniform4fv; - GLPtr fProgramUniform1iv; - GLPtr fProgramUniform2iv; - GLPtr fProgramUniform3iv; - GLPtr fProgramUniform4iv; - GLPtr fProgramUniformMatrix2fv; - GLPtr fProgramUniformMatrix3fv; - GLPtr fProgramUniformMatrix4fv; + GrGLFunction fProgramUniform1f; + GrGLFunction fProgramUniform2f; + GrGLFunction fProgramUniform3f; + GrGLFunction fProgramUniform4f; + GrGLFunction fProgramUniform1i; + GrGLFunction fProgramUniform2i; + GrGLFunction fProgramUniform3i; + GrGLFunction fProgramUniform4i; + GrGLFunction fProgramUniform1fv; + GrGLFunction fProgramUniform2fv; + GrGLFunction fProgramUniform3fv; + GrGLFunction fProgramUniform4fv; + GrGLFunction fProgramUniform1iv; + GrGLFunction fProgramUniform2iv; + GrGLFunction fProgramUniform3iv; + GrGLFunction fProgramUniform4iv; + GrGLFunction fProgramUniformMatrix2fv; + GrGLFunction fProgramUniformMatrix3fv; + GrGLFunction fProgramUniformMatrix4fv; // OpenGL 2.1 - GLPtr fProgramUniformMatrix2x3fv; - GLPtr fProgramUniformMatrix3x2fv; - GLPtr fProgramUniformMatrix2x4fv; - GLPtr fProgramUniformMatrix4x2fv; - GLPtr fProgramUniformMatrix3x4fv; - GLPtr fProgramUniformMatrix4x3fv; + GrGLFunction fProgramUniformMatrix2x3fv; + GrGLFunction fProgramUniformMatrix3x2fv; + GrGLFunction fProgramUniformMatrix2x4fv; + GrGLFunction fProgramUniformMatrix4x2fv; + GrGLFunction fProgramUniformMatrix3x4fv; + GrGLFunction fProgramUniformMatrix4x3fv; // OpenGL 3.0 - GLPtr fNamedRenderbufferStorage; - GLPtr fGetNamedRenderbufferParameteriv; - GLPtr fNamedRenderbufferStorageMultisample; - GLPtr fCheckNamedFramebufferStatus; - GLPtr fNamedFramebufferTexture1D; - GLPtr fNamedFramebufferTexture2D; - GLPtr fNamedFramebufferTexture3D; - GLPtr fNamedFramebufferRenderbuffer; - GLPtr fGetNamedFramebufferAttachmentParameteriv; - GLPtr fGenerateTextureMipmap; - GLPtr fFramebufferDrawBuffer; - GLPtr fFramebufferDrawBuffers; - GLPtr fFramebufferReadBuffer; - GLPtr fGetFramebufferParameteriv; - GLPtr fNamedCopyBufferSubData; - GLPtr fVertexArrayVertexOffset; - GLPtr fVertexArrayColorOffset; - GLPtr fVertexArrayEdgeFlagOffset; - GLPtr fVertexArrayIndexOffset; - GLPtr fVertexArrayNormalOffset; - GLPtr fVertexArrayTexCoordOffset; - GLPtr fVertexArrayMultiTexCoordOffset; - GLPtr fVertexArrayFogCoordOffset; - GLPtr fVertexArraySecondaryColorOffset; - GLPtr fVertexArrayVertexAttribOffset; - GLPtr fVertexArrayVertexAttribIOffset; - GLPtr fEnableVertexArray; - GLPtr fDisableVertexArray; - GLPtr fEnableVertexArrayAttrib; - GLPtr fDisableVertexArrayAttrib; - GLPtr fGetVertexArrayIntegerv; - GLPtr fGetVertexArrayPointerv; - GLPtr fGetVertexArrayIntegeri_v; - GLPtr fGetVertexArrayPointeri_v; - GLPtr fMapNamedBufferRange; - GLPtr fFlushMappedNamedBufferRange; + GrGLFunction fNamedRenderbufferStorage; + GrGLFunction fGetNamedRenderbufferParameteriv; + GrGLFunction fNamedRenderbufferStorageMultisample; + GrGLFunction fCheckNamedFramebufferStatus; + GrGLFunction fNamedFramebufferTexture1D; + GrGLFunction fNamedFramebufferTexture2D; + GrGLFunction fNamedFramebufferTexture3D; + GrGLFunction fNamedFramebufferRenderbuffer; + GrGLFunction fGetNamedFramebufferAttachmentParameteriv; + GrGLFunction fGenerateTextureMipmap; + GrGLFunction fFramebufferDrawBuffer; + GrGLFunction fFramebufferDrawBuffers; + GrGLFunction fFramebufferReadBuffer; + GrGLFunction fGetFramebufferParameteriv; + GrGLFunction fNamedCopyBufferSubData; + GrGLFunction fVertexArrayVertexOffset; + GrGLFunction fVertexArrayColorOffset; + GrGLFunction fVertexArrayEdgeFlagOffset; + GrGLFunction fVertexArrayIndexOffset; + GrGLFunction fVertexArrayNormalOffset; + GrGLFunction fVertexArrayTexCoordOffset; + GrGLFunction fVertexArrayMultiTexCoordOffset; + GrGLFunction fVertexArrayFogCoordOffset; + GrGLFunction fVertexArraySecondaryColorOffset; + GrGLFunction fVertexArrayVertexAttribOffset; + GrGLFunction fVertexArrayVertexAttribIOffset; + GrGLFunction fEnableVertexArray; + GrGLFunction fDisableVertexArray; + GrGLFunction fEnableVertexArrayAttrib; + GrGLFunction fDisableVertexArrayAttrib; + GrGLFunction fGetVertexArrayIntegerv; + GrGLFunction fGetVertexArrayPointerv; + GrGLFunction fGetVertexArrayIntegeri_v; + GrGLFunction fGetVertexArrayPointeri_v; + GrGLFunction fMapNamedBufferRange; + GrGLFunction fFlushMappedNamedBufferRange; + // OpenGL 3.1 + GrGLFunction fTextureBuffer; /* KHR_debug */ - GLPtr fDebugMessageControl; - GLPtr fDebugMessageInsert; - GLPtr fDebugMessageCallback; - GLPtr fGetDebugMessageLog; - GLPtr fPushDebugGroup; - GLPtr fPopDebugGroup; - GLPtr fObjectLabel; + GrGLFunction fDebugMessageControl; + GrGLFunction fDebugMessageInsert; + GrGLFunction fDebugMessageCallback; + GrGLFunction fGetDebugMessageLog; + GrGLFunction fPushDebugGroup; + GrGLFunction fPopDebugGroup; + GrGLFunction fObjectLabel; /* EGL functions */ - GLPtr fEGLCreateImage; - GLPtr fEGLDestroyImage; + GrGLFunction fEGLCreateImage; + GrGLFunction fEGLDestroyImage; } fFunctions; - // Per-GL func callback -#if GR_GL_PER_GL_FUNC_CALLBACK - GrGLInterfaceCallbackProc fCallback; - GrGLInterfaceCallbackData fCallbackData; -#endif - // This exists for internal testing. virtual void abandon() const {} }; diff --git a/gfx/skia/skia/include/gpu/gl/GrGLTypes.h b/gfx/skia/skia/include/gpu/gl/GrGLTypes.h index 248ce8886d..d0edcf101e 100644 --- a/gfx/skia/skia/include/gpu/gl/GrGLTypes.h +++ b/gfx/skia/skia/include/gpu/gl/GrGLTypes.h @@ -58,10 +58,40 @@ typedef signed long int GrGLintptr; typedef signed long int GrGLsizeiptr; #endif typedef void* GrGLeglImage; + +struct GrGLDrawArraysIndirectCommand { + GrGLuint fCount; + GrGLuint fInstanceCount; + GrGLuint fFirst; + GrGLuint fBaseInstance; // Requires EXT_base_instance on ES. +}; + +GR_STATIC_ASSERT(16 == sizeof(GrGLDrawArraysIndirectCommand)); + +struct GrGLDrawElementsIndirectCommand { + GrGLuint fCount; + GrGLuint fInstanceCount; + GrGLuint fFirstIndex; + GrGLuint fBaseVertex; + GrGLuint fBaseInstance; // Requires EXT_base_instance on ES. +}; + +GR_STATIC_ASSERT(20 == sizeof(GrGLDrawElementsIndirectCommand)); + +/** + * KHR_debug + */ +typedef void (GR_GL_FUNCTION_TYPE* GRGLDEBUGPROC)(GrGLenum source, + GrGLenum type, + GrGLuint id, + GrGLenum severity, + GrGLsizei length, + const GrGLchar* message, + const void* userParam); + /** * EGL types. */ - typedef void* GrEGLImage; typedef void* GrEGLDisplay; typedef void* GrEGLContext; diff --git a/gfx/skia/skia/include/gpu/gl/SkGLContext.h b/gfx/skia/skia/include/gpu/gl/SkGLContext.h deleted file mode 100644 index 77fd325dd8..0000000000 --- a/gfx/skia/skia/include/gpu/gl/SkGLContext.h +++ /dev/null @@ -1,136 +0,0 @@ - -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkGLContext_DEFINED -#define SkGLContext_DEFINED - -#include "GrGLInterface.h" -#include "../private/SkGpuFenceSync.h" - -/** - * Create an offscreen opengl context with an RGBA8 / 8bit stencil FBO. - * Provides a GrGLInterface struct of function pointers for the context. - * This class is intended for Skia's testing needs and not for general - * use. - */ -class SK_API SkGLContext : public SkNoncopyable { -public: - virtual ~SkGLContext(); - - bool isValid() const { return NULL != gl(); } - - const GrGLInterface* gl() const { return fGL.get(); } - - bool fenceSyncSupport() const { return SkToBool(fFenceSync); } - - bool getMaxGpuFrameLag(int* maxFrameLag) const { - if (!fFenceSync) { - return false; - } - *maxFrameLag = kMaxFrameLag; - return true; - } - - void makeCurrent() const; - - /** Used for testing EGLImage integration. Take a GL_TEXTURE_2D and wraps it in an EGL Image */ - virtual GrEGLImage texture2DToEGLImage(GrGLuint /*texID*/) const { return 0; } - virtual void destroyEGLImage(GrEGLImage) const {} - - /** Used for testing GL_TEXTURE_RECTANGLE integration. */ - GrGLint createTextureRectangle(int width, int height, GrGLenum internalFormat, - GrGLenum externalFormat, GrGLenum externalType, - GrGLvoid* data); - - /** - * Used for testing EGLImage integration. Takes a EGLImage and wraps it in a - * GL_TEXTURE_EXTERNAL_OES. - */ - virtual GrGLuint eglImageToExternalTexture(GrEGLImage) const { return 0; } - - /** - * The only purpose of this function it to provide a means of scheduling - * work on the GPU (since all of the subclasses create primary buffers for - * testing that are small and not meant to be rendered to the screen). - * - * If the platform supports fence sync (OpenGL 3.2+ or EGL_KHR_fence_sync), - * this will not swap any buffers, but rather emulate triple buffer - * synchronization using fences. - * - * Otherwise it will call the platform SwapBuffers method. This may or may - * not perform some sort of synchronization, depending on whether the - * drawing surface provided by the platform is double buffered. - */ - void swapBuffers(); - - /** - * This notifies the context that we are deliberately testing abandoning - * the context. It is useful for debugging contexts that would otherwise - * test that GPU resources are properly deleted. It also allows a debugging - * context to test that further GL calls are not made by Skia GPU code. - */ - void testAbandon(); - - /** - * Creates a new GL context of the same type and makes the returned context current - * (if not null). - */ - virtual SkGLContext* createNew() const { return nullptr; } - - class GLFenceSync; // SkGpuFenceSync implementation that uses the OpenGL functionality. - -protected: - SkGLContext(); - - /* - * Methods that sublcasses must call from their constructors and destructors. - */ - void init(const GrGLInterface*, SkGpuFenceSync* = NULL); - void teardown(); - - /* - * Operations that have a platform-dependent implementation. - */ - virtual void onPlatformMakeCurrent() const = 0; - virtual void onPlatformSwapBuffers() const = 0; - virtual GrGLFuncPtr onPlatformGetProcAddress(const char*) const = 0; - -private: - enum { kMaxFrameLag = 3 }; - - SkAutoTDelete fFenceSync; - SkPlatformGpuFence fFrameFences[kMaxFrameLag - 1]; - int fCurrentFenceIdx; - - /** Subclass provides the gl interface object if construction was - * successful. */ - SkAutoTUnref fGL; - - friend class GLFenceSync; // For onPlatformGetProcAddress. -}; - -/** Creates platform-dependent GL context object - * Returns a valid gl context object or NULL if such can not be created. - * Note: If Skia embedder needs a custom GL context that sets up the GL - * interface, this function should be implemented by the embedder. - * Otherwise, the default implementation for the platform should be compiled in - * the library. - */ -SK_API SkGLContext* SkCreatePlatformGLContext(GrGLStandard forcedGpuAPI); - -/** - * Helper macros for using the GL context through the GrGLInterface. Example: - * SK_GL(glCtx, GenTextures(1, &texID)); - */ -#define SK_GL(ctx, X) (ctx).gl()->fFunctions.f ## X; \ - SkASSERT(0 == (ctx).gl()->fFunctions.fGetError()) -#define SK_GL_RET(ctx, RET, X) (RET) = (ctx).gl()->fFunctions.f ## X; \ - SkASSERT(0 == (ctx).gl()->fFunctions.fGetError()) -#define SK_GL_NOERRCHECK(ctx, X) (ctx).gl()->fFunctions.f ## X -#define SK_GL_RET_NOERRCHECK(ctx, RET, X) (RET) = (ctx).gl()->fFunctions.f ## X - -#endif diff --git a/gfx/skia/skia/include/gpu/gl/SkNullGLContext.h b/gfx/skia/skia/include/gpu/gl/SkNullGLContext.h deleted file mode 100644 index 9e799a9fea..0000000000 --- a/gfx/skia/skia/include/gpu/gl/SkNullGLContext.h +++ /dev/null @@ -1,36 +0,0 @@ - -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkNullGLContext_DEFINED -#define SkNullGLContext_DEFINED - -#include "gl/SkGLContext.h" - -class SK_API SkNullGLContext : public SkGLContext { -public: - ~SkNullGLContext() override; - - static SkNullGLContext* Create(); - // FIXME: remove once Chromium has been updated. - static SkNullGLContext* Create(GrGLStandard forcedAPI) { - SkASSERT(forcedAPI == kNone_GrGLStandard); - (void)forcedAPI; return Create(); - } - - class ContextState; - -private: - SkNullGLContext(); - - void onPlatformMakeCurrent() const override; - void onPlatformSwapBuffers() const override {} - GrGLFuncPtr onPlatformGetProcAddress(const char*) const override { return NULL; } - - ContextState* fState; -}; - -#endif diff --git a/gfx/skia/skia/include/gpu/gl/angle/SkANGLEGLContext.h b/gfx/skia/skia/include/gpu/gl/angle/SkANGLEGLContext.h deleted file mode 100644 index ea5e877ca2..0000000000 --- a/gfx/skia/skia/include/gpu/gl/angle/SkANGLEGLContext.h +++ /dev/null @@ -1,61 +0,0 @@ - -/* - * Copyright 2012 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SkANGLEGLContext_DEFINED -#define SkANGLEGLContext_DEFINED - -#if SK_ANGLE - -#include "gl/SkGLContext.h" - -class SkANGLEGLContext : public SkGLContext { -public: - ~SkANGLEGLContext() override; -#ifdef SK_BUILD_FOR_WIN - static SkANGLEGLContext* CreateDirectX() { - SkANGLEGLContext* ctx = new SkANGLEGLContext(false); - if (!ctx->isValid()) { - delete ctx; - return NULL; - } - return ctx; - } -#endif - static SkANGLEGLContext* CreateOpenGL() { - SkANGLEGLContext* ctx = new SkANGLEGLContext(true); - if (!ctx->isValid()) { - delete ctx; - return NULL; - } - return ctx; - } - - GrEGLImage texture2DToEGLImage(GrGLuint texID) const override; - void destroyEGLImage(GrEGLImage) const override; - GrGLuint eglImageToExternalTexture(GrEGLImage) const override; - SkGLContext* createNew() const override; - - // The param is an EGLNativeDisplayType and the return is an EGLDispay. - static void* GetD3DEGLDisplay(void* nativeDisplay, bool useGLBackend); - -private: - SkANGLEGLContext(bool preferGLBackend); - void destroyGLContext(); - - void onPlatformMakeCurrent() const override; - void onPlatformSwapBuffers() const override; - GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override; - - void* fContext; - void* fDisplay; - void* fSurface; - bool fIsGLBackend; -}; - -#endif - -#endif diff --git a/gfx/skia/skia/include/gpu/gl/command_buffer/SkCommandBufferGLContext.h b/gfx/skia/skia/include/gpu/gl/command_buffer/SkCommandBufferGLContext.h deleted file mode 100644 index 47f3fd967a..0000000000 --- a/gfx/skia/skia/include/gpu/gl/command_buffer/SkCommandBufferGLContext.h +++ /dev/null @@ -1,63 +0,0 @@ - -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#ifndef SKCOMMANDBUFFERGLCONTEXT_DEFINED -#define SKCOMMANDBUFFERGLCONTEXT_DEFINED - -#if SK_COMMAND_BUFFER - -#include "gl/SkGLContext.h" - -class SkCommandBufferGLContext : public SkGLContext { -public: - ~SkCommandBufferGLContext() override; - - static SkCommandBufferGLContext* Create() { - SkCommandBufferGLContext* ctx = new SkCommandBufferGLContext; - if (!ctx->isValid()) { - delete ctx; - return nullptr; - } - return ctx; - } - - static SkCommandBufferGLContext* Create(void* nativeWindow, int msaaSampleCount) { - SkCommandBufferGLContext* ctx = new SkCommandBufferGLContext(nativeWindow, - msaaSampleCount); - if (!ctx->isValid()) { - delete ctx; - return nullptr; - } - return ctx; - } - - void presentCommandBuffer(); - - bool makeCurrent(); - int getStencilBits(); - int getSampleCount(); - -private: - SkCommandBufferGLContext(); - SkCommandBufferGLContext(void* nativeWindow, int msaaSampleCount); - void initializeGLContext(void* nativeWindow, const int* configAttribs, - const int* surfaceAttribs); - void destroyGLContext(); - - void onPlatformMakeCurrent() const override; - void onPlatformSwapBuffers() const override; - GrGLFuncPtr onPlatformGetProcAddress(const char* name) const override; - - void* fContext; - void* fDisplay; - void* fSurface; - void* fConfig; -}; - -#endif // SK_COMMAND_BUFFER - -#endif diff --git a/gfx/skia/skia/include/gpu/vk/GrVkBackendContext.h b/gfx/skia/skia/include/gpu/vk/GrVkBackendContext.h new file mode 100644 index 0000000000..82a9f94bcc --- /dev/null +++ b/gfx/skia/skia/include/gpu/vk/GrVkBackendContext.h @@ -0,0 +1,60 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrVkBackendContext_DEFINED +#define GrVkBackendContext_DEFINED + +#include "SkRefCnt.h" + +#include "vk/GrVkDefines.h" + +#ifdef SK_DEBUG +#define ENABLE_VK_LAYERS +#endif + +struct GrVkInterface; + +enum GrVkExtensionFlags { + kEXT_debug_report_GrVkExtensionFlag = 0x0001, + kNV_glsl_shader_GrVkExtensionFlag = 0x0002, + kKHR_surface_GrVkExtensionFlag = 0x0004, + kKHR_swapchain_GrVkExtensionFlag = 0x0008, + kKHR_win32_surface_GrVkExtensionFlag = 0x0010, + kKHR_android_surface_GrVkExtensionFlag = 0x0020, + kKHR_xlib_surface_GrVkExtensionFlag = 0x0040, +}; + +enum GrVkFeatureFlags { + kGeometryShader_GrVkFeatureFlag = 0x0001, + kDualSrcBlend_GrVkFeatureFlag = 0x0002, + kSampleRateShading_GrVkFeatureFlag = 0x0004, +}; + +// The BackendContext contains all of the base Vulkan objects needed by the GrVkGpu. The assumption +// is that the client will set these up and pass them to the GrVkGpu constructor. The VkDevice +// created must support at least one graphics queue, which is passed in as well. +// The QueueFamilyIndex must match the family of the given queue. It is needed for CommandPool +// creation, and any GrBackendObjects handed to us (e.g., for wrapped textures) need to be created +// in or transitioned to that family. +struct GrVkBackendContext : public SkRefCnt { + VkInstance fInstance; + VkPhysicalDevice fPhysicalDevice; + VkDevice fDevice; + VkQueue fQueue; + uint32_t fQueueFamilyIndex; + uint32_t fMinAPIVersion; + uint32_t fExtensions; + uint32_t fFeatures; + SkAutoTUnref fInterface; + + // Helper function to create the default Vulkan objects needed by the GrVkGpu object + static const GrVkBackendContext* Create(); + + ~GrVkBackendContext() override; +}; + +#endif diff --git a/gfx/skia/skia/include/gpu/vk/GrVkDefines.h b/gfx/skia/skia/include/gpu/vk/GrVkDefines.h new file mode 100644 index 0000000000..7ff81c84f9 --- /dev/null +++ b/gfx/skia/skia/include/gpu/vk/GrVkDefines.h @@ -0,0 +1,22 @@ + +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrVkDefines_DEFINED +#define GrVkDefines_DEFINED + +#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_WIN32) +#define VK_USE_PLATFORM_WIN32_KHR +#elif defined(SK_BUILD_FOR_ANDROID) +#define VK_USE_PLATFORM_ANDROID_KHR +#elif defined(SK_BUILD_FOR_UNIX) +#define VK_USE_PLATFORM_XLIB_KHR +#endif + +#include + +#endif diff --git a/gfx/skia/skia/include/gpu/vk/GrVkInterface.h b/gfx/skia/skia/include/gpu/vk/GrVkInterface.h new file mode 100644 index 0000000000..418aaae958 --- /dev/null +++ b/gfx/skia/skia/include/gpu/vk/GrVkInterface.h @@ -0,0 +1,232 @@ +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrVkInterface_DEFINED +#define GrVkInterface_DEFINED + +#include "SkRefCnt.h" + +#include "vk/GrVkDefines.h" + +//////////////////////////////////////////////////////////////////////////////// + +/** + * The default interface is returned by GrVkCreateInterface. This function's + * implementation is platform-specific. + */ + +struct GrVkInterface; + +/** + * Creates a GrVkInterface. + */ +const GrVkInterface* GrVkCreateInterface(VkInstance instance, VkDevice device, + uint32_t extensionFlags); + + +/** + * GrContext uses the following interface to make all calls into Vulkan. When a + * GrContext is created it is given a GrVkInterface. All functions that should be + * available based on the Vulkan's version must be non-NULL or GrContext creation + * will fail. This can be tested with the validate() method. + */ +struct SK_API GrVkInterface : public SkRefCnt { +private: + // simple wrapper class that exists only to initialize a pointer to NULL + template class VkPtr { + public: + VkPtr() : fPtr(NULL) {} + VkPtr operator=(FNPTR_TYPE ptr) { fPtr = ptr; return *this; } + operator FNPTR_TYPE() const { return fPtr; } + private: + FNPTR_TYPE fPtr; + }; + + typedef SkRefCnt INHERITED; + +public: + GrVkInterface(); + + // Validates that the GrVkInterface supports its advertised standard. This means the necessary + // function pointers have been initialized for Vulkan version. + bool validate() const; + + /** + * The function pointers are in a struct so that we can have a compiler generated assignment + * operator. + */ + struct Functions { + VkPtr fCreateInstance; + VkPtr fDestroyInstance; + VkPtr fEnumeratePhysicalDevices; + VkPtr fGetPhysicalDeviceFeatures; + VkPtr fGetPhysicalDeviceFormatProperties; + VkPtr fGetPhysicalDeviceImageFormatProperties; + VkPtr fGetPhysicalDeviceProperties; + VkPtr fGetPhysicalDeviceQueueFamilyProperties; + VkPtr fGetPhysicalDeviceMemoryProperties; + VkPtr fCreateDevice; + VkPtr fDestroyDevice; + VkPtr fEnumerateInstanceExtensionProperties; + VkPtr fEnumerateDeviceExtensionProperties; + VkPtr fEnumerateInstanceLayerProperties; + VkPtr fEnumerateDeviceLayerProperties; + VkPtr fGetDeviceQueue; + VkPtr fQueueSubmit; + VkPtr fQueueWaitIdle; + VkPtr fDeviceWaitIdle; + VkPtr fAllocateMemory; + VkPtr fFreeMemory; + VkPtr fMapMemory; + VkPtr fUnmapMemory; + VkPtr fFlushMappedMemoryRanges; + VkPtr fInvalidateMappedMemoryRanges; + VkPtr fGetDeviceMemoryCommitment; + VkPtr fBindBufferMemory; + VkPtr fBindImageMemory; + VkPtr fGetBufferMemoryRequirements; + VkPtr fGetImageMemoryRequirements; + VkPtr fGetImageSparseMemoryRequirements; + VkPtr fGetPhysicalDeviceSparseImageFormatProperties; + VkPtr fQueueBindSparse; + VkPtr fCreateFence; + VkPtr fDestroyFence; + VkPtr fResetFences; + VkPtr fGetFenceStatus; + VkPtr fWaitForFences; + VkPtr fCreateSemaphore; + VkPtr fDestroySemaphore; + VkPtr fCreateEvent; + VkPtr fDestroyEvent; + VkPtr fGetEventStatus; + VkPtr fSetEvent; + VkPtr fResetEvent; + VkPtr fCreateQueryPool; + VkPtr fDestroyQueryPool; + VkPtr fGetQueryPoolResults; + VkPtr fCreateBuffer; + VkPtr fDestroyBuffer; + VkPtr fCreateBufferView; + VkPtr fDestroyBufferView; + VkPtr fCreateImage; + VkPtr fDestroyImage; + VkPtr fGetImageSubresourceLayout; + VkPtr fCreateImageView; + VkPtr fDestroyImageView; + VkPtr fCreateShaderModule; + VkPtr fDestroyShaderModule; + VkPtr fCreatePipelineCache; + VkPtr fDestroyPipelineCache; + VkPtr fGetPipelineCacheData; + VkPtr fMergePipelineCaches; + VkPtr fCreateGraphicsPipelines; + VkPtr fCreateComputePipelines; + VkPtr fDestroyPipeline; + VkPtr fCreatePipelineLayout; + VkPtr fDestroyPipelineLayout; + VkPtr fCreateSampler; + VkPtr fDestroySampler; + VkPtr fCreateDescriptorSetLayout; + VkPtr fDestroyDescriptorSetLayout; + VkPtr fCreateDescriptorPool; + VkPtr fDestroyDescriptorPool; + VkPtr fResetDescriptorPool; + VkPtr fAllocateDescriptorSets; + VkPtr fFreeDescriptorSets; + VkPtr fUpdateDescriptorSets; + VkPtr fCreateFramebuffer; + VkPtr fDestroyFramebuffer; + VkPtr fCreateRenderPass; + VkPtr fDestroyRenderPass; + VkPtr fGetRenderAreaGranularity; + VkPtr fCreateCommandPool; + VkPtr fDestroyCommandPool; + VkPtr fResetCommandPool; + VkPtr fAllocateCommandBuffers; + VkPtr fFreeCommandBuffers; + VkPtr fBeginCommandBuffer; + VkPtr fEndCommandBuffer; + VkPtr fResetCommandBuffer; + VkPtr fCmdBindPipeline; + VkPtr fCmdSetViewport; + VkPtr fCmdSetScissor; + VkPtr fCmdSetLineWidth; + VkPtr fCmdSetDepthBias; + VkPtr fCmdSetBlendConstants; + VkPtr fCmdSetDepthBounds; + VkPtr fCmdSetStencilCompareMask; + VkPtr fCmdSetStencilWriteMask; + VkPtr fCmdSetStencilReference; + VkPtr fCmdBindDescriptorSets; + VkPtr fCmdBindIndexBuffer; + VkPtr fCmdBindVertexBuffers; + VkPtr fCmdDraw; + VkPtr fCmdDrawIndexed; + VkPtr fCmdDrawIndirect; + VkPtr fCmdDrawIndexedIndirect; + VkPtr fCmdDispatch; + VkPtr fCmdDispatchIndirect; + VkPtr fCmdCopyBuffer; + VkPtr fCmdCopyImage; + VkPtr fCmdBlitImage; + VkPtr fCmdCopyBufferToImage; + VkPtr fCmdCopyImageToBuffer; + VkPtr fCmdUpdateBuffer; + VkPtr fCmdFillBuffer; + VkPtr fCmdClearColorImage; + VkPtr fCmdClearDepthStencilImage; + VkPtr fCmdClearAttachments; + VkPtr fCmdResolveImage; + VkPtr fCmdSetEvent; + VkPtr fCmdResetEvent; + VkPtr fCmdWaitEvents; + VkPtr fCmdPipelineBarrier; + VkPtr fCmdBeginQuery; + VkPtr fCmdEndQuery; + VkPtr fCmdResetQueryPool; + VkPtr fCmdWriteTimestamp; + VkPtr fCmdCopyQueryPoolResults; + VkPtr fCmdPushConstants; + VkPtr fCmdBeginRenderPass; + VkPtr fCmdNextSubpass; + VkPtr fCmdEndRenderPass; + VkPtr fCmdExecuteCommands; + VkPtr fDestroySurfaceKHR; + VkPtr fGetPhysicalDeviceSurfaceSupportKHR; + VkPtr fGetPhysicalDeviceSurfaceCapabilitiesKHR; + VkPtr fGetPhysicalDeviceSurfaceFormatsKHR; + VkPtr fGetPhysicalDeviceSurfacePresentModesKHR; +#if defined(VK_USE_PLATFORM_WIN32_KHR) + VkPtr fCreateWin32SurfaceKHR; + VkPtr fGetPhysicalDeviceWin32PresentationSupportKHR; +#elif defined(VK_USE_PLATFORM_ANDROID_KHR) + VkPtr fCreateAndroidSurfaceKHR; +#elif defined(VK_USE_PLATFORM_XLIB_KHR) + VkPtr fCreateXlibSurfaceKHR; + VkPtr fGetPhysicalDeviceXlibPresentationSupportKHR; +#endif + VkPtr fCreateSwapchainKHR; + VkPtr fDestroySwapchainKHR; + VkPtr fGetSwapchainImagesKHR; + VkPtr fAcquireNextImageKHR; + VkPtr fQueuePresentKHR; + VkPtr fGetPhysicalDeviceDisplayPropertiesKHR; + VkPtr fGetPhysicalDeviceDisplayPlanePropertiesKHR; + VkPtr fGetDisplayPlaneSupportedDisplaysKHR; + VkPtr fGetDisplayModePropertiesKHR; + VkPtr fCreateDisplayModeKHR; + VkPtr fGetDisplayPlaneCapabilitiesKHR; + VkPtr fCreateDisplayPlaneSurfaceKHR; + VkPtr fCreateSharedSwapchainsKHR; + VkPtr fCreateDebugReportCallbackEXT; + VkPtr fDebugReportMessageEXT; + VkPtr fDestroyDebugReportCallbackEXT; + } fFunctions; + +}; + +#endif diff --git a/gfx/skia/skia/include/gpu/vk/GrVkTypes.h b/gfx/skia/skia/include/gpu/vk/GrVkTypes.h new file mode 100644 index 0000000000..833f6d30df --- /dev/null +++ b/gfx/skia/skia/include/gpu/vk/GrVkTypes.h @@ -0,0 +1,42 @@ + +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrVkTypes_DEFINED +#define GrVkTypes_DEFINED + +#include "vk/GrVkDefines.h" + +/** + * KHR_debug + */ +/*typedef void (GR_GL_FUNCTION_TYPE* GrVkDEBUGPROC)(GrVkenum source, + GrVkenum type, + GrVkuint id, + GrVkenum severity, + GrVksizei length, + const GrVkchar* message, + const void* userParam);*/ + + + +/////////////////////////////////////////////////////////////////////////////// +/** + * Types for interacting with Vulkan resources created externally to Skia. GrBackendObjects for + * Vulkan textures are really const GrVkTextureInfo* + */ + +struct GrVkTextureInfo { + VkImage fImage; + VkDeviceMemory fAlloc; // this may be null iff the texture is an RT and uses borrow semantics + VkImageTiling fImageTiling; + VkImageLayout fImageLayout; +}; + +GR_STATIC_ASSERT(sizeof(GrBackendObject) >= sizeof(const GrVkTextureInfo*)); + +#endif diff --git a/gfx/skia/skia/include/images/SkDecodingImageGenerator.h b/gfx/skia/skia/include/images/SkDecodingImageGenerator.h deleted file mode 100644 index 0a5ec56fe8..0000000000 --- a/gfx/skia/skia/include/images/SkDecodingImageGenerator.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2013 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkDecodingImageGenerator_DEFINED -#define SkDecodingImageGenerator_DEFINED - -#include "SkBitmap.h" -#include "SkImageGenerator.h" - -class SkData; -class SkStreamRewindable; - -/** - * An implementation of SkImageGenerator that calls into - * SkImageDecoder. - */ -namespace SkDecodingImageGenerator { - /** - * These options will be passed on to the image decoder. The - * defaults are sensible. - * - * @param fSampleSize If set to > 1, tells the decoder to return a - * smaller than original bitmap, sampling 1 pixel for - * every size pixels. e.g. if sample size is set to 3, - * then the returned bitmap will be 1/3 as wide and high, - * and will contain 1/9 as many pixels as the original. - * Note: this is a hint, and the codec may choose to - * ignore this, or only approximate the sample size. - * - * @param fDitherImage Set to true if the the decoder should try to - * dither the resulting image when decoding to a smaller - * color-space. The default is true. - * - * @param fRequestedColorType If not given, then use whichever - * config the decoder wants. Else try to use this color - * type. If the decoder won't support this color type, - * SkDecodingImageGenerator::Create will return - * NULL. kIndex_8_SkColorType is not supported. - * - * @param fRequireUnpremul If true, the decoder will attempt to - * decode without premultiplying the alpha. If it cannot, - * the pixels will be set to NULL. - */ - struct Options { - Options() - : fSampleSize(1) - , fDitherImage(true) - , fUseRequestedColorType(false) - , fRequestedColorType() - , fRequireUnpremul(false) { } - Options(int sampleSize, bool dither) - : fSampleSize(sampleSize) - , fDitherImage(dither) - , fUseRequestedColorType(false) - , fRequestedColorType() - , fRequireUnpremul(false) { } - Options(int sampleSize, bool dither, SkColorType colorType) - : fSampleSize(sampleSize) - , fDitherImage(dither) - , fUseRequestedColorType(true) - , fRequestedColorType(colorType) - , fRequireUnpremul(false) { } - Options(int sampleSize, bool dither, SkColorType colorType, - bool requireUnpremul) - : fSampleSize(sampleSize) - , fDitherImage(dither) - , fUseRequestedColorType(true) - , fRequestedColorType(colorType) - , fRequireUnpremul(requireUnpremul) { } - const int fSampleSize; - const bool fDitherImage; - const bool fUseRequestedColorType; - const SkColorType fRequestedColorType; - const bool fRequireUnpremul; - }; - - /** - * These two functions return a SkImageGenerator that calls into - * SkImageDecoder. They return NULL on failure. - * - * The SkData version of this function is preferred. If the stream - * has an underlying SkData (such as a SkMemoryStream) pass that in. - * - * This object, if non-NULL, takes ownership of stream and deletes stream - * upon deletion. If NULL is returned, stream is deleted immediately. - * - * @param Options (see above) - * - * @return NULL on failure, a new SkImageGenerator on success. - */ - SkImageGenerator* Create(SkStreamRewindable* stream, - const Options& opt); - - /** - * @param data Contains the encoded image data that will be used by - * the SkDecodingImageGenerator. Will be ref()ed by the - * SkImageGenerator constructor and and unref()ed on deletion. - */ - SkImageGenerator* Create(SkData* data, const Options& opt); -}; - -// // Example of most basic use case: -// -// bool install_data(SkData* data, SkBitmap* dst) { -// return SkInstallDiscardablePixelRef( -// SkDecodingImageGenerator::Create( -// data, SkDecodingImageGenerator::Options()), dst, NULL); -// } -// bool install_stream(SkStreamRewindable* stream, SkBitmap* dst) { -// return SkInstallDiscardablePixelRef( -// SkDecodingImageGenerator::Create( -// stream, SkDecodingImageGenerator::Options()), dst, NULL); -// } - -#endif // SkDecodingImageGenerator_DEFINED diff --git a/gfx/skia/skia/include/images/SkPageFlipper.h b/gfx/skia/skia/include/images/SkPageFlipper.h deleted file mode 100644 index 7779c30160..0000000000 --- a/gfx/skia/skia/include/images/SkPageFlipper.h +++ /dev/null @@ -1,62 +0,0 @@ - -/* - * Copyright 2008 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkPageFlipper_DEFINED -#define SkPageFlipper_DEFINED - -#include "SkRegion.h" - -/** SkPageFlipper manages alternating inval/dirty regions for a rectangular area - (like a bitmap). You call inval() to accumulate inval areas, and then when - you're ready to "flip" pages (i.e. draw into the one you've been - invalidating) you call update, which swaps the inval regions, and returns - two things to you: 1) the final inval region to be drawn into, and 2) the - region of pixels that should be copied from the "front" page onto the one - you're about to draw into. This copyBits region will be disjoint from the - inval region, so both need to be handled. - */ -class SkPageFlipper { -public: - SkPageFlipper(); - SkPageFlipper(int width, int height); - - int width() const { return fWidth; } - int height() const { return fHeight; } - - void resize(int width, int height); - - bool isDirty() const { return !fDirty1->isEmpty(); } - const SkRegion& dirtyRgn() const { return *fDirty1; } - - void inval(); - void inval(const SkIRect&); - void inval(const SkRegion&); - void inval(const SkRect&, bool antialias); - - /** When you're ready to write to the back page, call update. The returned - region is the invalidate are that needs to be drawn to. The copyBits - region (provided by the caller) is the area that should be copied from - the front page to the back page (will not intersect with the returned - inval region. - - Once this is called, the two internal regions are swapped, so the *new* - back inval region is ready to receive new inval calls. - */ - const SkRegion& update(SkRegion* copyBits); - -private: - SkRegion* fDirty0; - SkRegion* fDirty1; - SkRegion fDirty0Storage; - SkRegion fDirty1Storage; - int fWidth; - int fHeight; -}; - -#endif diff --git a/gfx/skia/skia/include/pathops/SkPathOps.h b/gfx/skia/skia/include/pathops/SkPathOps.h index 047588b635..fa01788394 100644 --- a/gfx/skia/skia/include/pathops/SkPathOps.h +++ b/gfx/skia/skia/include/pathops/SkPathOps.h @@ -7,9 +7,9 @@ #ifndef SkPathOps_DEFINED #define SkPathOps_DEFINED +#include "../private/SkTArray.h" +#include "../private/SkTDArray.h" #include "SkPreConfig.h" -#include "SkTArray.h" -#include "SkTDArray.h" class SkPath; struct SkRect; diff --git a/gfx/skia/skia/include/ports/SkFontConfigInterface.h b/gfx/skia/skia/include/ports/SkFontConfigInterface.h index 304961fd78..063cb9d553 100644 --- a/gfx/skia/skia/include/ports/SkFontConfigInterface.h +++ b/gfx/skia/skia/include/ports/SkFontConfigInterface.h @@ -11,7 +11,6 @@ #include "SkDataTable.h" #include "SkFontStyle.h" #include "SkRefCnt.h" -#include "SkTArray.h" #include "SkTypeface.h" struct SkBaseMutex; @@ -116,11 +115,6 @@ public: // New APIS, which have default impls for now (which do nothing) virtual SkDataTable* getFamilyNames() { return SkDataTable::NewEmpty(); } - virtual bool matchFamilySet(const char[] /*inFamilyName*/, - SkString* /*outFamilyName*/, - SkTArray*) { - return false; - } typedef SkRefCnt INHERITED; }; diff --git a/gfx/skia/skia/include/ports/SkFontMgr.h b/gfx/skia/skia/include/ports/SkFontMgr.h index 96a8501c48..3e6c785be9 100644 --- a/gfx/skia/skia/include/ports/SkFontMgr.h +++ b/gfx/skia/skia/include/ports/SkFontMgr.h @@ -8,8 +8,10 @@ #ifndef SkFontMgr_DEFINED #define SkFontMgr_DEFINED -#include "SkRefCnt.h" #include "SkFontStyle.h" +#include "SkRefCnt.h" +#include "SkScalar.h" +#include "SkTypes.h" class SkData; class SkFontData; @@ -100,6 +102,52 @@ public: */ SkTypeface* createFromStream(SkStreamAsset*, int ttcIndex = 0) const; + struct FontParameters { + struct Axis { + SkFourByteTag fTag; + SkScalar fStyleValue; + }; + + FontParameters() : fCollectionIndex(0), fAxisCount(0), fAxes(nullptr) {} + + /** Specify the index of the desired font. + * + * Font formats like ttc, dfont, cff, cid, pfr, t42, t1, and fon may actually be indexed + * collections of fonts. + */ + FontParameters& setCollectionIndex(int collectionIndex) { + fCollectionIndex = collectionIndex; + return *this; + } + + /** Specify the GX variation axis values. + * + * Any axes not specified will use the default value. Specified axes not present in the + * font will be ignored. + * + * @param axes not copied. This pointer must remain valid for life of FontParameters. + */ + FontParameters& setAxes(const Axis* axes, int axisCount) { + fAxisCount = axisCount; + fAxes = axes; + return *this; + } + + int getCollectionIndex() const { + return fCollectionIndex; + } + const Axis* getAxes(int* axisCount) const { + *axisCount = fAxisCount; + return fAxes; + } + private: + int fCollectionIndex; + int fAxisCount; + const Axis* fAxes; + }; + /* Experimental, API subject to change. */ + SkTypeface* createFromStream(SkStreamAsset*, const FontParameters&) const; + /** * Create a typeface from the specified font data. * Takes ownership of the font data, so the caller should not reference it again. @@ -144,6 +192,7 @@ protected: virtual SkTypeface* onCreateFromData(SkData*, int ttcIndex) const = 0; virtual SkTypeface* onCreateFromStream(SkStreamAsset*, int ttcIndex) const = 0; // TODO: make pure virtual. + virtual SkTypeface* onCreateFromStream(SkStreamAsset*, const FontParameters&) const; virtual SkTypeface* onCreateFromFontData(SkFontData*) const; virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const = 0; diff --git a/gfx/skia/skia/include/ports/SkFontMgr_android.h b/gfx/skia/skia/include/ports/SkFontMgr_android.h index e489b161b2..f12f51f36a 100644 --- a/gfx/skia/skia/include/ports/SkFontMgr_android.h +++ b/gfx/skia/skia/include/ports/SkFontMgr_android.h @@ -39,6 +39,11 @@ struct SkFontMgr_Android_CustomFonts { * In the new style (version > 21) fontsXml format is used, this should be NULL. */ const char* fFallbackFontsXml; + + /** Optional custom flag. If set to true the SkFontMgr will acquire all requisite + * system IO resources on initialization. + */ + bool fIsolated; }; /** Create a font manager for Android. If 'custom' is NULL, use only system fonts. */ diff --git a/gfx/skia/skia/include/ports/SkFontMgr_custom.h b/gfx/skia/skia/include/ports/SkFontMgr_custom.h index ad37686ebb..53be63db1b 100644 --- a/gfx/skia/skia/include/ports/SkFontMgr_custom.h +++ b/gfx/skia/skia/include/ports/SkFontMgr_custom.h @@ -15,4 +15,7 @@ class SkFontMgr; /** Create a custom font manager which scans a given directory for font files. */ SK_API SkFontMgr* SkFontMgr_New_Custom_Directory(const char* dir); +/** Create a custom font manager that contains no built-in fonts. */ +SK_API SkFontMgr* SkFontMgr_New_Custom_Empty(); + #endif // SkFontMgr_custom_DEFINED diff --git a/gfx/skia/skia/include/ports/SkFontMgr_indirect.h b/gfx/skia/skia/include/ports/SkFontMgr_indirect.h index 81cd7739a9..37502d2404 100644 --- a/gfx/skia/skia/include/ports/SkFontMgr_indirect.h +++ b/gfx/skia/skia/include/ports/SkFontMgr_indirect.h @@ -8,12 +8,12 @@ #ifndef SkFontMgr_indirect_DEFINED #define SkFontMgr_indirect_DEFINED +#include "../private/SkMutex.h" +#include "../private/SkTArray.h" #include "SkDataTable.h" #include "SkFontMgr.h" -#include "../private/SkMutex.h" #include "SkRefCnt.h" #include "SkRemotableFontMgr.h" -#include "SkTArray.h" #include "SkTypeface.h" #include "SkTypes.h" @@ -70,8 +70,7 @@ private: DataEntry() { } - // This is a move!!! - DataEntry(DataEntry& that) + DataEntry(DataEntry&& that) : fDataId(that.fDataId) , fTtcIndex(that.fTtcIndex) , fTypeface(that.fTypeface) diff --git a/gfx/skia/skia/include/ports/SkTypeface_win.h b/gfx/skia/skia/include/ports/SkTypeface_win.h index 72104555d4..88d69a6807 100644 --- a/gfx/skia/skia/include/ports/SkTypeface_win.h +++ b/gfx/skia/skia/include/ports/SkTypeface_win.h @@ -41,6 +41,7 @@ SK_API void SkTypeface_SetEnsureLOGFONTAccessibleProc(void (*)(const LOGFONT&)); class SkFontMgr; class SkRemotableFontMgr; struct IDWriteFactory; +struct IDWriteFontCollection; /** * Like the other Typeface create methods, this returns a new reference to the @@ -53,7 +54,8 @@ SK_API SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFactory* aFactory, IDWriteFontFamily* aFontFamily); SK_API SkFontMgr* SkFontMgr_New_GDI(); -SK_API SkFontMgr* SkFontMgr_New_DirectWrite(IDWriteFactory* factory = NULL); +SK_API SkFontMgr* SkFontMgr_New_DirectWrite(IDWriteFactory* factory = NULL, + IDWriteFontCollection* collection = NULL); /** * Creates an SkFontMgr which renders using DirectWrite and obtains its data diff --git a/gfx/skia/skia/include/private/GrAuditTrail.h b/gfx/skia/skia/include/private/GrAuditTrail.h index fbaec84bb3..3bb7bea435 100644 --- a/gfx/skia/skia/include/private/GrAuditTrail.h +++ b/gfx/skia/skia/include/private/GrAuditTrail.h @@ -12,105 +12,175 @@ #include "SkRect.h" #include "SkString.h" #include "SkTArray.h" +#include "SkTHash.h" + +class GrBatch; /* * GrAuditTrail collects a list of draw ops, detailed information about those ops, and can dump them * to json. + * + * Capturing this information is expensive and consumes a lot of memory, therefore it is important + * to enable auditing only when required and disable it promptly. The AutoEnable class helps to + * ensure that the audit trail is disabled in a timely fashion. Once the information has been dealt + * with, be sure to call reset(), or the log will simply keep growing. */ class GrAuditTrail { public: - GrAuditTrail() : fUniqueID(0) {} + GrAuditTrail() + : fClientID(kGrAuditTrailInvalidID) + , fEnabled(false) {} - class AutoFrame { + class AutoEnable { public: - AutoFrame(GrAuditTrail* auditTrail, const char* name) + AutoEnable(GrAuditTrail* auditTrail) : fAuditTrail(auditTrail) { - if (GR_BATCH_DEBUGGING_OUTPUT) { - fAuditTrail->pushFrame(name); - } + SkASSERT(!fAuditTrail->isEnabled()); + fAuditTrail->setEnabled(true); } - ~AutoFrame() { - if (GR_BATCH_DEBUGGING_OUTPUT) { - fAuditTrail->popFrame(); - } + ~AutoEnable() { + SkASSERT(fAuditTrail->isEnabled()); + fAuditTrail->setEnabled(false); } private: GrAuditTrail* fAuditTrail; }; - void pushFrame(const char* name) { - SkASSERT(GR_BATCH_DEBUGGING_OUTPUT); - Frame* frame = new Frame; - if (fStack.empty()) { - fFrames.emplace_back(frame); - } else { - fStack.back()->fChildren.emplace_back(frame); + class AutoManageBatchList { + public: + AutoManageBatchList(GrAuditTrail* auditTrail) + : fAutoEnable(auditTrail) + , fAuditTrail(auditTrail) { } - frame->fUniqueID = fUniqueID++; - frame->fName = name; - fStack.push_back(frame); + ~AutoManageBatchList() { + fAuditTrail->fullReset(); + } + + private: + AutoEnable fAutoEnable; + GrAuditTrail* fAuditTrail; + }; + + class AutoCollectBatches { + public: + AutoCollectBatches(GrAuditTrail* auditTrail, int clientID) + : fAutoEnable(auditTrail) + , fAuditTrail(auditTrail) { + fAuditTrail->setClientID(clientID); + } + + ~AutoCollectBatches() { fAuditTrail->setClientID(kGrAuditTrailInvalidID); } + + private: + AutoEnable fAutoEnable; + GrAuditTrail* fAuditTrail; + }; + + void pushFrame(const char* framename) { + SkASSERT(fEnabled); + fCurrentStackTrace.push_back(SkString(framename)); } - void popFrame() { - SkASSERT(GR_BATCH_DEBUGGING_OUTPUT); - fStack.pop_back(); - } + void addBatch(const GrBatch* batch); - void addBatch(const char* name, const SkRect& bounds) { - SkASSERT(GR_BATCH_DEBUGGING_OUTPUT && !fStack.empty()); - Batch* batch = new Batch; - fStack.back()->fChildren.emplace_back(batch); - batch->fName = name; - batch->fBounds = bounds; - } + void batchingResultCombined(const GrBatch* consumer, const GrBatch* consumed); - SkString toJson() const; + // Because batching is heavily dependent on sequence of draw calls, these calls will only + // produce valid information for the given draw sequence which preceeded them. + // Specifically, future draw calls may change the batching and thus would invalidate + // the json. What this means is that for some sequence of draw calls N, the below toJson + // calls will only produce JSON which reflects N draw calls. This JSON may or may not be + // accurate for N + 1 or N - 1 draws depending on the actual batching algorithm used. + SkString toJson(bool prettyPrint = false) const; - void reset() { SkASSERT(GR_BATCH_DEBUGGING_OUTPUT && fStack.empty()); fFrames.reset(); } + // returns a json string of all of the batches associated with a given client id + SkString toJson(int clientID, bool prettyPrint = false) const; + + bool isEnabled() { return fEnabled; } + void setEnabled(bool enabled) { fEnabled = enabled; } + + void setClientID(int clientID) { fClientID = clientID; } + + // We could just return our internal bookkeeping struct if copying the data out becomes + // a performance issue, but until then its nice to decouple + struct BatchInfo { + SkRect fBounds; + uint32_t fRenderTargetUniqueID; + struct Batch { + int fClientID; + SkRect fBounds; + }; + SkTArray fBatches; + }; + + void getBoundsByClientID(SkTArray* outInfo, int clientID); + void getBoundsByBatchListID(BatchInfo* outInfo, int batchListID); + + void fullReset(); + + static const int kGrAuditTrailInvalidID; private: // TODO if performance becomes an issue, we can move to using SkVarAlloc - struct Event { - virtual ~Event() {} - virtual SkString toJson() const=0; - - const char* fName; - uint64_t fUniqueID; - }; - - typedef SkTArray, true> FrameArray; - struct Frame : public Event { - SkString toJson() const override; - FrameArray fChildren; - }; - - struct Batch : public Event { - SkString toJson() const override; + struct Batch { + SkString toJson() const; + SkString fName; + SkTArray fStackTrace; SkRect fBounds; + int fClientID; + int fBatchListID; + int fChildID; }; + typedef SkTArray, true> BatchPool; - static void JsonifyTArray(SkString* json, const char* name, const FrameArray& array); + typedef SkTArray Batches; - FrameArray fFrames; - SkTArray fStack; - uint64_t fUniqueID; + struct BatchNode { + SkString toJson() const; + SkRect fBounds; + Batches fChildren; + uint32_t fRenderTargetUniqueID; + }; + typedef SkTArray, true> BatchList; + + void copyOutFromBatchList(BatchInfo* outBatchInfo, int batchListID); + + template + static void JsonifyTArray(SkString* json, const char* name, const T& array, + bool addComma); + + BatchPool fBatchPool; + SkTHashMap fIDLookup; + SkTHashMap fClientIDLookup; + BatchList fBatchList; + SkTArray fCurrentStackTrace; + + // The client cas pass in an optional client ID which we will use to mark the batches + int fClientID; + bool fEnabled; }; -#define GR_AUDIT_TRAIL_INVOKE_GUARD(invoke, ...) \ - if (GR_BATCH_DEBUGGING_OUTPUT) { \ - invoke(__VA_ARGS__); \ +#define GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, invoke, ...) \ + if (audit_trail->isEnabled()) { \ + audit_trail->invoke(__VA_ARGS__); \ } #define GR_AUDIT_TRAIL_AUTO_FRAME(audit_trail, framename) \ - GrAuditTrail::AutoFrame SK_MACRO_APPEND_LINE(auto_frame)(audit_trail, framename); + GR_AUDIT_TRAIL_INVOKE_GUARD((audit_trail), pushFrame, framename); #define GR_AUDIT_TRAIL_RESET(audit_trail) \ - GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail->reset); + //GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, fullReset); -#define GR_AUDIT_TRAIL_ADDBATCH(audit_trail, batchname, bounds) \ - GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail->addBatch, batchname, bounds); +#define GR_AUDIT_TRAIL_ADDBATCH(audit_trail, batch) \ + GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, addBatch, batch); + +#define GR_AUDIT_TRAIL_BATCHING_RESULT_COMBINED(audit_trail, combineWith, batch) \ + GR_AUDIT_TRAIL_INVOKE_GUARD(audit_trail, batchingResultCombined, combineWith, batch); + +#define GR_AUDIT_TRAIL_BATCHING_RESULT_NEW(audit_trail, batch) \ + // Doesn't do anything now, one day... #endif diff --git a/gfx/skia/skia/include/private/SkAtomics.h b/gfx/skia/skia/include/private/SkAtomics.h index 0ec3ce415c..d9989cf0f7 100644 --- a/gfx/skia/skia/include/private/SkAtomics.h +++ b/gfx/skia/skia/include/private/SkAtomics.h @@ -58,6 +58,10 @@ public: sk_atomic_store(&fVal, val, mo); } + T exchange(const T& val, sk_memory_order mo = default_memory_order) { + return sk_atomic_exchange(&fVal, val, mo); + } + // Alias for .load(default_memory_order). MOZ_IMPLICIT operator T() const { return this->load(); diff --git a/gfx/skia/skia/include/core/SkFixed.h b/gfx/skia/skia/include/private/SkFixed.h similarity index 78% rename from gfx/skia/skia/include/core/SkFixed.h rename to gfx/skia/skia/include/private/SkFixed.h index f6dd3d6002..c90d5e9ffd 100644 --- a/gfx/skia/skia/include/core/SkFixed.h +++ b/gfx/skia/skia/include/private/SkFixed.h @@ -8,6 +8,9 @@ #ifndef SkFixed_DEFINED #define SkFixed_DEFINED +#include "SkScalar.h" +#include "math.h" + #include "SkTypes.h" /** \file SkFixed.h @@ -22,22 +25,24 @@ typedef int32_t SkFixed; #define SK_FixedHalf (1 << 15) #define SK_FixedMax (0x7FFFFFFF) #define SK_FixedMin (-SK_FixedMax) -#define SK_FixedNaN ((int) 0x80000000) #define SK_FixedPI (0x3243F) #define SK_FixedSqrt2 (92682) #define SK_FixedTanPIOver8 (0x6A0A) #define SK_FixedRoot2Over2 (0xB505) #define SkFixedToFloat(x) ((x) * 1.52587890625e-5f) -#if 1 - #define SkFloatToFixed(x) ((SkFixed)((x) * SK_Fixed1)) -#else - // pins over/under flows to max/min int32 (slower than just a cast) - static inline SkFixed SkFloatToFixed(float x) { - int64_t n = x * SK_Fixed1; - return (SkFixed)n; - } -#endif +#define SkFloatToFixed(x) ((SkFixed)((x) * SK_Fixed1)) + +// Pins over/under flows to SK_FixedMax/SK_FixedMin (slower than just a cast). +static inline SkFixed SkFloatPinToFixed(float x) { + x *= SK_Fixed1; + // Casting float to int outside the range of the target type (int32_t) is undefined behavior. + if (x >= SK_FixedMax) return SK_FixedMax; + if (x <= SK_FixedMin) return SK_FixedMin; + const SkFixed result = static_cast(x); + SkASSERT(truncf(x) == static_cast(result)); + return result; +} #ifdef SK_DEBUG static inline SkFixed SkFloatToFixed_Check(float x) { @@ -53,6 +58,17 @@ typedef int32_t SkFixed; #define SkFixedToDouble(x) ((x) * 1.52587890625e-5) #define SkDoubleToFixed(x) ((SkFixed)((x) * SK_Fixed1)) +// Pins over/under flows to SK_FixedMax/SK_FixedMin (slower than just a cast). +static inline SkFixed SkDoublePinToFixed(double x) { + x *= SK_Fixed1; + // Casting double to int outside the range of the target type (int32_t) is undefined behavior. + if (x >= SK_FixedMax) return SK_FixedMax; + if (x <= SK_FixedMin) return SK_FixedMin; + const SkFixed result = static_cast(x); + SkASSERT(trunc(x) == static_cast(result)); + return result; +} + /** Converts an integer to a SkFixed, asserting that the result does not overflow a 32 bit signed integer */ @@ -143,6 +159,22 @@ inline SkFixed SkFixedMul_longlong(SkFixed a, SkFixed b) { /////////////////////////////////////////////////////////////////////////////// +#if SK_SCALAR_IS_FLOAT + +#define SkFixedToScalar(x) SkFixedToFloat(x) +#define SkScalarToFixed(x) SkFloatToFixed(x) +#define SkScalarPinToFixed(x) SkFloatPinToFixed(x) + +#else // SK_SCALAR_IS_DOUBLE + +#define SkFixedToScalar(x) SkFixedToDouble(x) +#define SkScalarToFixed(x) SkDoubleToFixed(x) +#define SkScalarPinToFixed(x) SkDoublePinToFixed(x) + +#endif + +/////////////////////////////////////////////////////////////////////////////// + typedef int64_t SkFixed3232; // 32.32 #define SkIntToFixed3232(x) (SkLeftShift((SkFixed3232)(x), 32)) @@ -153,19 +185,4 @@ typedef int64_t SkFixed3232; // 32.32 #define SkScalarToFixed3232(x) SkFloatToFixed3232(x) -/////////////////////////////////////////////////////////////////////////////// - -// 64bits wide, with a 16bit bias. Useful when accumulating lots of 16.16 so -// we don't overflow along the way -typedef int64_t Sk48Dot16; - -#define Sk48Dot16FloorToInt(x) static_cast((x) >> 16) - -static inline float Sk48Dot16ToScalar(Sk48Dot16 x) { - return static_cast(x * 1.5258789e-5); // x * (1.0f / (1 << 16)) -} -#define SkFloatTo48Dot16(x) (static_cast((x) * (1 << 16))) - -#define SkScalarTo48Dot16(x) SkFloatTo48Dot16(x) - #endif diff --git a/gfx/skia/skia/include/private/SkFloatBits.h b/gfx/skia/skia/include/private/SkFloatBits.h index 3ddb9ef564..caad342a4a 100644 --- a/gfx/skia/skia/include/private/SkFloatBits.h +++ b/gfx/skia/skia/include/private/SkFloatBits.h @@ -32,7 +32,7 @@ static inline int32_t Sk2sComplimentToSignBit(int32_t x) { // make x positive x = (x ^ sign) - sign; // set the sign bit as needed - x |= sign << 31; + x |= SkLeftShift(sign, 31); return x; } diff --git a/gfx/skia/skia/include/private/SkFloatingPoint.h b/gfx/skia/skia/include/private/SkFloatingPoint.h index 225e6ca958..25b18c95ca 100644 --- a/gfx/skia/skia/include/private/SkFloatingPoint.h +++ b/gfx/skia/skia/include/private/SkFloatingPoint.h @@ -29,39 +29,13 @@ static inline float sk_float_pow(float base, float exp) { return powf(base, exp); } -static inline float sk_float_copysign(float x, float y) { -// c++11 contains a 'float copysign(float, float)' function in . -// clang-cl reports __cplusplus for clang, not the __cplusplus vc++ version _MSC_VER would report. -#if (defined(_MSC_VER) && defined(__clang__)) -# define SK_BUILD_WITH_CLANG_CL 1 -#else -# define SK_BUILD_WITH_CLANG_CL 0 -#endif -#if (!SK_BUILD_WITH_CLANG_CL && __cplusplus >= 201103L) || (_MSC_VER >= 1800) - return copysignf(x, y); - -// Posix has demanded 'float copysignf(float, float)' (from C99) since Issue 6. -#elif defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L - return copysignf(x, y); - -// Visual studio prior to 13 only has 'double _copysign(double, double)'. -#elif defined(_MSC_VER) - return (float)_copysign(x, y); - -// Otherwise convert to bits and extract sign. -#else - int32_t xbits = SkFloat2Bits(x); - int32_t ybits = SkFloat2Bits(y); - return SkBits2Float((xbits & 0x7FFFFFFF) | (ybits & 0x80000000)); -#endif -} - #define sk_float_sqrt(x) sqrtf(x) #define sk_float_sin(x) sinf(x) #define sk_float_cos(x) cosf(x) #define sk_float_tan(x) tanf(x) #define sk_float_floor(x) floorf(x) #define sk_float_ceil(x) ceilf(x) +#define sk_float_trunc(x) truncf(x) #ifdef SK_BUILD_FOR_MAC # define sk_float_acos(x) static_cast(acos(x)) # define sk_float_asin(x) static_cast(asin(x)) @@ -71,6 +45,7 @@ static inline float sk_float_copysign(float x, float y) { #endif #define sk_float_atan2(y,x) atan2f(y,x) #define sk_float_abs(x) fabsf(x) +#define sk_float_copysign(x, y) copysignf(x, y) #define sk_float_mod(x,y) fmodf(x,y) #define sk_float_exp(x) expf(x) #define sk_float_log(x) logf(x) @@ -127,20 +102,28 @@ extern const uint32_t gIEEENegativeInfinity; #define SK_FloatInfinity (*SkTCast(&gIEEEInfinity)) #define SK_FloatNegativeInfinity (*SkTCast(&gIEEENegativeInfinity)) -// We forward declare this to break an #include cycle. -// (SkScalar -> SkFloatingPoint -> SkOpts.h -> SkXfermode -> SkColor -> SkScalar) -namespace SkOpts { extern float (*rsqrt)(float); } +static inline float sk_float_rsqrt_portable(float x) { + // Get initial estimate. + int i = *SkTCast(&x); + i = 0x5F1FFFF9 - (i>>1); + float estimate = *SkTCast(&i); + + // One step of Newton's method to refine. + const float estimate_sq = estimate*estimate; + estimate *= 0.703952253f*(2.38924456f-x*estimate_sq); + return estimate; +} // Fast, approximate inverse square root. // Compare to name-brand "1.0f / sk_float_sqrt(x)". Should be around 10x faster on SSE, 2x on NEON. -static inline float sk_float_rsqrt(const float x) { +static inline float sk_float_rsqrt(float x) { // We want all this inlined, so we'll inline SIMD and just take the hit when we don't know we've got // it at compile time. This is going to be too fast to productively hide behind a function pointer. // -// We do one step of Newton's method to refine the estimates in the NEON and null paths. No +// We do one step of Newton's method to refine the estimates in the NEON and portable paths. No // refinement is faster, but very innacurate. Two steps is more accurate, but slower than 1/sqrt. // -// Optimized constants in the null path courtesy of http://rrrola.wz.cz/inv_sqrt.html +// Optimized constants in the portable path courtesy of http://rrrola.wz.cz/inv_sqrt.html #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE1 return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(x))); #elif defined(SK_ARM_HAS_NEON) @@ -153,8 +136,7 @@ static inline float sk_float_rsqrt(const float x) { estimate = vmul_f32(estimate, vrsqrts_f32(xx, estimate_sq)); return vget_lane_f32(estimate, 0); // 1 will work fine too; the answer's in both places. #else - // Perhaps runtime-detected NEON, or a portable fallback. - return SkOpts::rsqrt(x); + return sk_float_rsqrt_portable(x); #endif } diff --git a/gfx/skia/skia/include/private/SkGpuFenceSync.h b/gfx/skia/skia/include/private/SkGpuFenceSync.h index b78398fed8..72fd241078 100644 --- a/gfx/skia/skia/include/private/SkGpuFenceSync.h +++ b/gfx/skia/skia/include/private/SkGpuFenceSync.h @@ -20,7 +20,7 @@ typedef void* SkPlatformGpuFence; class SkGpuFenceSync { public: virtual SkPlatformGpuFence SK_WARN_UNUSED_RESULT insertFence() const = 0; - virtual bool flushAndWaitFence(SkPlatformGpuFence) const = 0; + virtual bool waitFence(SkPlatformGpuFence, bool flush) const = 0; virtual void deleteFence(SkPlatformGpuFence) const = 0; virtual ~SkGpuFenceSync() {} diff --git a/gfx/skia/skia/include/private/SkMiniRecorder.h b/gfx/skia/skia/include/private/SkMiniRecorder.h index 5b68fc22f8..2f5612ca39 100644 --- a/gfx/skia/skia/include/private/SkMiniRecorder.h +++ b/gfx/skia/skia/include/private/SkMiniRecorder.h @@ -27,7 +27,7 @@ public: bool drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, const SkPaint&); // Detach anything we've recorded as a picture, resetting this SkMiniRecorder. - SkPicture* detachAsPicture(const SkRect& cull); + sk_sp detachAsPicture(const SkRect& cull); // Flush anything we've recorded to the canvas, resetting this SkMiniRecorder. // This is logically the same as but rather more efficient than: diff --git a/gfx/skia/skia/include/private/SkOnce.h b/gfx/skia/skia/include/private/SkOnce.h index a3ef4d6713..5434d9d7d9 100644 --- a/gfx/skia/skia/include/private/SkOnce.h +++ b/gfx/skia/skia/include/private/SkOnce.h @@ -59,7 +59,7 @@ public: private: bool fDone; - SkPODSpinlock fSpinlock; + SkSpinlock fSpinlock; }; // We've pulled a pretty standard double-checked locking implementation apart diff --git a/gfx/skia/skia/include/private/SkOncePtr.h b/gfx/skia/skia/include/private/SkOncePtr.h index 261f9a7ef4..3c1ab634ee 100644 --- a/gfx/skia/skia/include/private/SkOncePtr.h +++ b/gfx/skia/skia/include/private/SkOncePtr.h @@ -9,7 +9,7 @@ #define SkOncePtr_DEFINED #include "../private/SkAtomics.h" -#include "SkUniquePtr.h" +#include template class SkBaseOncePtr; @@ -17,7 +17,7 @@ template class SkBaseOncePtr; #define SK_DECLARE_STATIC_ONCE_PTR(type, name) namespace {} static SkBaseOncePtr name; // Use this for a local or member pointer that's initialized exactly once when you call get(). -template > +template > class SkOncePtr : SkNoncopyable { public: SkOncePtr() { sk_bzero(this, sizeof(*this)); } @@ -42,7 +42,7 @@ private: // If you ask for SkOncePtr, we'll clean up with delete[] by default. template -class SkOncePtr : public SkOncePtr> {}; +class SkOncePtr : public SkOncePtr> {}; /* TODO(mtklein): in next CL typedef SkBaseOncePtr SkOnceFlag; diff --git a/gfx/skia/skia/include/private/SkRecords.h b/gfx/skia/skia/include/private/SkRecords.h index ecd73a12d9..1e274e9f46 100644 --- a/gfx/skia/skia/include/private/SkRecords.h +++ b/gfx/skia/skia/include/private/SkRecords.h @@ -8,6 +8,7 @@ #ifndef SkRecords_DEFINED #define SkRecords_DEFINED +#include "SkData.h" #include "SkCanvas.h" #include "SkDrawable.h" #include "SkImageFilter.h" @@ -17,6 +18,7 @@ #include "SkRect.h" #include "SkRRect.h" #include "SkRSXform.h" +#include "SkString.h" #include "SkTextBlob.h" namespace SkRecords { @@ -66,7 +68,8 @@ namespace SkRecords { M(DrawRect) \ M(DrawTextBlob) \ M(DrawAtlas) \ - M(DrawVertices) + M(DrawVertices) \ + M(DrawAnnotation) // Defines SkRecords::Type, an enum of all record types. #define ENUM(T) T##_Type, @@ -358,7 +361,10 @@ RECORD(DrawVertices, kDraw_Tag, RefBox xmode; PODArray indices; int indexCount); - +RECORD(DrawAnnotation, 0, + SkRect rect; + SkString key; + RefBox value); #undef RECORD } // namespace SkRecords diff --git a/gfx/skia/skia/include/private/SkSpinlock.h b/gfx/skia/skia/include/private/SkSpinlock.h index ef52ab013a..da3b6dd511 100644 --- a/gfx/skia/skia/include/private/SkSpinlock.h +++ b/gfx/skia/skia/include/private/SkSpinlock.h @@ -5,40 +5,31 @@ * found in the LICENSE file. */ -// This file is not part of the public Skia API. - #ifndef SkSpinlock_DEFINED #define SkSpinlock_DEFINED -#include "../private/SkAtomics.h" +#include "SkTypes.h" +#include "SkAtomics.h" -#define SK_DECLARE_STATIC_SPINLOCK(name) namespace {} static SkPODSpinlock name - -// This class has no constructor and must be zero-initialized (the macro above does this). -class SK_API SkPODSpinlock { +class SkSpinlock { public: void acquire() { - // To act as a mutex, we need an acquire barrier if we take the lock. - if (sk_atomic_exchange(&fLocked, true, sk_memory_order_acquire)) { + // To act as a mutex, we need an acquire barrier when we acquire the lock. + if (fLocked.exchange(true, sk_memory_order_acquire)) { // Lock was contended. Fall back to an out-of-line spin loop. this->contendedAcquire(); } } void release() { - // To act as a mutex, we need a release barrier. - sk_atomic_store(&fLocked, false, sk_memory_order_release); + // To act as a mutex, we need a release barrier when we release the lock. + fLocked.store(false, sk_memory_order_release); } private: - void contendedAcquire(); - bool fLocked; -}; + SK_API void contendedAcquire(); -// For non-global-static use cases, this is normally what you want. -class SkSpinlock : public SkPODSpinlock { -public: - SkSpinlock() { this->release(); } + SkAtomic fLocked{false}; }; #endif//SkSpinlock_DEFINED diff --git a/gfx/skia/skia/include/core/SkTArray.h b/gfx/skia/skia/include/private/SkTArray.h similarity index 80% rename from gfx/skia/skia/include/core/SkTArray.h rename to gfx/skia/skia/include/private/SkTArray.h index 17cbc5c05c..e83cd5390a 100644 --- a/gfx/skia/skia/include/core/SkTArray.h +++ b/gfx/skia/skia/include/private/SkTArray.h @@ -8,57 +8,19 @@ #ifndef SkTArray_DEFINED #define SkTArray_DEFINED -#include "../private/SkTemplates.h" #include "SkTypes.h" +#include "../private/SkTLogic.h" +#include "../private/SkTemplates.h" #include #include -template class SkTArray; - -namespace SkTArrayExt { - -template -inline void copy(SkTArray* self, int dst, int src) { - memcpy(&self->fItemArray[dst], &self->fItemArray[src], sizeof(T)); -} -template -inline void copy(SkTArray* self, const T* array) { - sk_careful_memcpy(self->fMemArray, array, self->fCount * sizeof(T)); -} -template -inline void copyAndDelete(SkTArray* self, char* newMemArray) { - sk_careful_memcpy(newMemArray, self->fMemArray, self->fCount * sizeof(T)); -} - -template -inline void copy(SkTArray* self, int dst, int src) { - new (&self->fItemArray[dst]) T(self->fItemArray[src]); -} -template -inline void copy(SkTArray* self, const T* array) { - for (int i = 0; i < self->fCount; ++i) { - new (self->fItemArray + i) T(array[i]); - } -} -template -inline void copyAndDelete(SkTArray* self, char* newMemArray) { - for (int i = 0; i < self->fCount; ++i) { - new (newMemArray + sizeof(T) * i) T(self->fItemArray[i]); - self->fItemArray[i].~T(); - } -} - -} - -template void* operator new(size_t, SkTArray*, int); - /** When MEM_COPY is true T will be bit copied when moved. When MEM_COPY is false, T will be copy constructed / destructed. In all cases T will be default-initialized on allocation, and its destructor will be called from this object's destructor. */ -template class SkTArray { +template class SkTArray { public: /** * Creates an empty array with no initial storage @@ -105,7 +67,7 @@ public: fCount = 0; this->checkRealloc((int)array.count()); fCount = array.count(); - SkTArrayExt::copy(this, static_cast(array.fMemArray)); + this->copy(static_cast(array.fMemArray)); return *this; } @@ -150,7 +112,7 @@ public: int delta = count - fCount; this->checkRealloc(delta); fCount = count; - SkTArrayExt::copy(this, array); + this->copy(array); } void removeShuffle(int n) { @@ -159,8 +121,7 @@ public: fCount = newCount; fItemArray[n].~T(); if (n != newCount) { - SkTArrayExt::copy(this, n, newCount); - fItemArray[newCount].~T(); + this->move(n, newCount); } } @@ -194,6 +155,15 @@ public: return *newT; } + /** + * Version of above that uses a move constructor to initialize the new item + */ + T& push_back(T&& t) { + T* newT = reinterpret_cast(this->push_back_raw(1)); + new (newT) T(std::move(t)); + return *newT; + } + /** * Construct a new T at the back of this array. */ @@ -224,7 +194,7 @@ public: SkASSERT(n >= 0); T* newTs = reinterpret_cast(this->push_back_raw(n)); for (int i = 0; i < n; ++i) { - new (newTs[i]) T(t); + new (newTs + i) T(t); } return newTs; } @@ -422,10 +392,38 @@ protected: fMemArray = sk_malloc_throw(fAllocCount * sizeof(T)); } - SkTArrayExt::copy(this, array); + this->copy(array); } private: + /** In the following move and copy methods, 'dst' is assumed to be uninitialized raw storage. + * In the following move methods, 'src' is destroyed leaving behind uninitialized raw storage. + */ + template SK_WHEN(E, void) copy(const T* src) { + sk_careful_memcpy(fMemArray, src, fCount * sizeof(T)); + } + template SK_WHEN(E, void) move(int dst, int src) { + memcpy(&fItemArray[dst], &fItemArray[src], sizeof(T)); + } + template SK_WHEN(E, void) move(char* dst) { + sk_careful_memcpy(dst, fMemArray, fCount * sizeof(T)); + } + + template SK_WHEN(!E, void) copy(const T* src) { + for (int i = 0; i < fCount; ++i) { + new (fItemArray + i) T(src[i]); + } + } + template SK_WHEN(!E, void) move(int dst, int src) { + new (&fItemArray[dst]) T(std::move(fItemArray[src])); + fItemArray[src].~T(); + } + template SK_WHEN(!E, void) move(char* dst) { + for (int i = 0; i < fCount; ++i) { + new (dst + sizeof(T) * i) T(std::move(fItemArray[i])); + fItemArray[i].~T(); + } + } static const int gMIN_ALLOC_COUNT = 8; @@ -463,7 +461,7 @@ private: newMemArray = (char*) sk_malloc_throw(fAllocCount*sizeof(T)); } - SkTArrayExt::copyAndDelete(this, newMemArray); + this->move(newMemArray); if (fMemArray != fPreAllocMemArray) { sk_free(fMemArray); @@ -472,16 +470,6 @@ private: } } - friend void* operator new(size_t, SkTArray*, int); - - template friend void SkTArrayExt::copy(SkTArray* that, int dst, int src); - template friend void SkTArrayExt::copy(SkTArray* that, const X*); - template friend void SkTArrayExt::copyAndDelete(SkTArray* that, char*); - - template friend void SkTArrayExt::copy(SkTArray* that, int dst, int src); - template friend void SkTArrayExt::copy(SkTArray* that, const X*); - template friend void SkTArrayExt::copyAndDelete(SkTArray* that, char*); - int fReserveCount; int fCount; int fAllocCount; @@ -492,29 +480,6 @@ private: }; }; -// Use the below macro (SkNEW_APPEND_TO_TARRAY) rather than calling this directly -template -void* operator new(size_t, SkTArray* array, int SkDEBUGCODE(atIndex)) { - // Currently, we only support adding to the end of the array. When the array class itself - // supports random insertion then this should be updated. - // SkASSERT(atIndex >= 0 && atIndex <= array->count()); - SkASSERT(atIndex == array->count()); - return array->push_back_raw(1); -} - -// Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete -// to match the op new silences warnings about missing op delete when a constructor throws an -// exception. -template -void operator delete(void*, SkTArray* /*array*/, int /*atIndex*/) { - SK_CRASH(); -} - -// Constructs a new object as the last element of an SkTArray. -#define SkNEW_APPEND_TO_TARRAY(array_ptr, type_name, args) \ - (new ((array_ptr), (array_ptr)->count()) type_name args) - - /** * Subclass of SkTArray that contains a preallocated memory block for the array. */ diff --git a/gfx/skia/skia/include/core/SkTDArray.h b/gfx/skia/skia/include/private/SkTDArray.h similarity index 99% rename from gfx/skia/skia/include/core/SkTDArray.h rename to gfx/skia/skia/include/private/SkTDArray.h index 0b71beed55..d5a46d900b 100644 --- a/gfx/skia/skia/include/core/SkTDArray.h +++ b/gfx/skia/skia/include/private/SkTDArray.h @@ -70,7 +70,7 @@ public: /** Return a ptr to the array of data, to be freed with sk_free. This also resets the SkTDArray to be empty. */ - T* detach() { + T* release() { T* array = fArray; fArray = NULL; fReserve = fCount = 0; diff --git a/gfx/skia/skia/include/private/SkTFitsIn.h b/gfx/skia/skia/include/private/SkTFitsIn.h new file mode 100644 index 0000000000..8e8c2b1ba9 --- /dev/null +++ b/gfx/skia/skia/include/private/SkTFitsIn.h @@ -0,0 +1,226 @@ +/* + * Copyright 2013 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTFitsIn_DEFINED +#define SkTFitsIn_DEFINED + +#include "../private/SkTLogic.h" +#include + +namespace sktfitsin { +namespace Private { + +/** SkTMux::type = (a && b) ? Both : (a) ? A : (b) ? B : Neither; */ +template +struct SkTMux { + using type = skstd::conditional_t, + skstd::conditional_t>; +}; + +/** SkTHasMoreDigits = (digits(A) >= digits(B)) ? true_type : false_type. */ +template struct SkTHasMoreDigits + : skstd::bool_constant::digits >= std::numeric_limits::digits> +{ }; + +/** A high or low side predicate which is used when it is statically known + * that source values are in the range of the Destination. + */ +template struct SkTOutOfRange_False { + using can_be_true = std::false_type; + using source_type = S; + static bool apply(S) { + return false; + } +}; + +/** A low side predicate which tests if the source value < Min(D). + * Assumes that Min(S) <= Min(D). + */ +template struct SkTOutOfRange_LT_MinD { + using can_be_true = std::true_type; + using source_type = S; + static bool apply(S s) { + using precondition = SkTHasMoreDigits; + static_assert(precondition::value, "minS > minD"); + + return s < static_cast((std::numeric_limits::min)()); + } +}; + +/** A low side predicate which tests if the source value is less than 0. */ +template struct SkTOutOfRange_LT_Zero { + using can_be_true = std::true_type; + using source_type = S; + static bool apply(S s) { + return s < static_cast(0); + } +}; + +/** A high side predicate which tests if the source value > Max(D). + * Assumes that Max(S) >= Max(D). + */ +template struct SkTOutOfRange_GT_MaxD { + using can_be_true = std::true_type; + using source_type = S; + static bool apply(S s) { + using precondition = SkTHasMoreDigits; + static_assert(precondition::value, "maxS < maxD"); + + return s > static_cast((std::numeric_limits::max)()); + } +}; + +/** Composes two SkTOutOfRange predicates. + * First checks OutOfRange_Low then, if in range, OutOfRange_High. + */ +template struct SkTOutOfRange_Either { + using can_be_true = std::true_type; + using source_type = typename OutOfRange_Low::source_type; + static bool apply(source_type s) { + bool outOfRange = OutOfRange_Low::apply(s); + if (!outOfRange) { + outOfRange = OutOfRange_High::apply(s); + } + return outOfRange; + } +}; + +/** SkTCombineOutOfRange::type is an SkTOutOfRange_XXX type which is the + * optimal combination of OutOfRange_Low and OutOfRange_High. + */ +template struct SkTCombineOutOfRange { + using Both = SkTOutOfRange_Either; + using Neither = SkTOutOfRange_False; + + using apply_low = typename OutOfRange_Low::can_be_true; + using apply_high = typename OutOfRange_High::can_be_true; + + using type = typename SkTMux::type; +}; + +template +struct SkTRangeChecker { + /** This is the method which is called at runtime to do the range check. */ + static bool OutOfRange(S s) { + using Combined = typename SkTCombineOutOfRange::type; + return Combined::apply(s); + } +}; + +/** SkTFitsIn_Unsigned2Unsiged::type is an SkTRangeChecker with an OutOfRange(S s) method + * the implementation of which is tailored for the source and destination types. + * Assumes that S and D are unsigned integer types. + */ +template struct SkTFitsIn_Unsigned2Unsiged { + using OutOfRange_Low = SkTOutOfRange_False; + using OutOfRange_High = SkTOutOfRange_GT_MaxD; + + using HighSideOnlyCheck = SkTRangeChecker; + using NoCheck = SkTRangeChecker, SkTOutOfRange_False>; + + // If std::numeric_limits::digits >= std::numeric_limits::digits, nothing to check. + // This also protects the precondition of SkTOutOfRange_GT_MaxD. + using sourceFitsInDesitination = SkTHasMoreDigits; + using type = skstd::conditional_t; +}; + +/** SkTFitsIn_Signed2Signed::type is an SkTRangeChecker with an OutOfRange(S s) method + * the implementation of which is tailored for the source and destination types. + * Assumes that S and D are signed integer types. + */ +template struct SkTFitsIn_Signed2Signed { + using OutOfRange_Low = SkTOutOfRange_LT_MinD; + using OutOfRange_High = SkTOutOfRange_GT_MaxD; + + using FullCheck = SkTRangeChecker; + using NoCheck = SkTRangeChecker, SkTOutOfRange_False>; + + // If std::numeric_limits::digits >= std::numeric_limits::digits, nothing to check. + // This also protects the precondition of SkTOutOfRange_LT_MinD and SkTOutOfRange_GT_MaxD. + using sourceFitsInDesitination = SkTHasMoreDigits; + using type = skstd::conditional_t; +}; + +/** SkTFitsIn_Signed2Unsigned::type is an SkTRangeChecker with an OutOfRange(S s) method + * the implementation of which is tailored for the source and destination types. + * Assumes that S is a signed integer type and D is an unsigned integer type. + */ +template struct SkTFitsIn_Signed2Unsigned { + using OutOfRange_Low = SkTOutOfRange_LT_Zero; + using OutOfRange_High = SkTOutOfRange_GT_MaxD; + + using FullCheck = SkTRangeChecker; + using LowSideOnlyCheck = SkTRangeChecker>; + + // If std::numeric_limits::max() >= std::numeric_limits::max(), + // no need to check the high side. (Until C++11, assume more digits means greater max.) + // This also protects the precondition of SkTOutOfRange_GT_MaxD. + using sourceCannotExceedDest = SkTHasMoreDigits; + using type = skstd::conditional_t; +}; + +/** SkTFitsIn_Unsigned2Signed::type is an SkTRangeChecker with an OutOfRange(S s) method + * the implementation of which is tailored for the source and destination types. + * Assumes that S is an usigned integer type and D is a signed integer type. + */ +template struct SkTFitsIn_Unsigned2Signed { + using OutOfRange_Low = SkTOutOfRange_False; + using OutOfRange_High = SkTOutOfRange_GT_MaxD; + + using HighSideOnlyCheck = SkTRangeChecker; + using NoCheck = SkTRangeChecker, SkTOutOfRange_False>; + + // If std::numeric_limits::max() >= std::numeric_limits::max(), nothing to check. + // (Until C++11, assume more digits means greater max.) + // This also protects the precondition of SkTOutOfRange_GT_MaxD. + using sourceCannotExceedDest = SkTHasMoreDigits; + using type = skstd::conditional_t; +}; + +/** SkTFitsIn::type is an SkTRangeChecker with an OutOfRange(S s) method + * the implementation of which is tailored for the source and destination types. + * Assumes that S and D are integer types. + */ +template struct SkTFitsIn { + // One of the following will be the 'selector' type. + using S2S = SkTFitsIn_Signed2Signed; + using S2U = SkTFitsIn_Signed2Unsigned; + using U2S = SkTFitsIn_Unsigned2Signed; + using U2U = SkTFitsIn_Unsigned2Unsiged; + + using S_is_signed = skstd::bool_constant::is_signed>; + using D_is_signed = skstd::bool_constant::is_signed>; + + using selector = typename SkTMux::type; + // This type is an SkTRangeChecker. + using type = typename selector::type; +}; + +template ::value> struct underlying_type { + using type = skstd::underlying_type_t; +}; +template struct underlying_type { + using type = T; +}; + +} // namespace Private +} // namespace sktfitsin + +/** Returns true if the integer source value 's' will fit in the integer destination type 'D'. */ +template inline bool SkTFitsIn(S s) { + static_assert(std::is_integral::value || std::is_enum::value, "S must be integral."); + static_assert(std::is_integral::value || std::is_enum::value, "D must be integral."); + + using RealS = typename sktfitsin::Private::underlying_type::type; + using RealD = typename sktfitsin::Private::underlying_type::type; + + return !sktfitsin::Private::SkTFitsIn::type::OutOfRange(s); +} + +#endif diff --git a/gfx/skia/skia/include/private/SkTLogic.h b/gfx/skia/skia/include/private/SkTLogic.h index 5754d5d536..240edaf839 100644 --- a/gfx/skia/skia/include/private/SkTLogic.h +++ b/gfx/skia/skia/include/private/SkTLogic.h @@ -14,57 +14,58 @@ #ifndef SkTLogic_DEFINED #define SkTLogic_DEFINED -#include "SkTypes.h" - -#include +#include #include #include - -#if SKIA_IMPLEMENTATION +#include #include -#endif +#include -#ifdef MOZ_SKIA +#if MOZ_SKIA_AVOID_CXX11 +#include "mozilla/Function.h" #include "mozilla/Move.h" #include "mozilla/TypeTraits.h" - -#if SKIA_IMPLEMENTATION -#include "mozilla/Function.h" -#endif - -// In libc++, symbols such as std::forward may be defined in std::__1. -// The _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD macros -// will expand to the correct namespace. -#ifdef _LIBCPP_BEGIN_NAMESPACE_STD -#define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD -#define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD -#else -#define MOZ_BEGIN_STD_NAMESPACE namespace std { -#define MOZ_END_STD_NAMESPACE } -#endif - -MOZ_BEGIN_STD_NAMESPACE - using mozilla::Forward; - #define forward Forward -MOZ_END_STD_NAMESPACE +#include "mozilla/UniquePtr.h" namespace std { -#if SKIA_IMPLEMENTATION - using mozilla::IntegralConstant; - using mozilla::IsEmpty; - using mozilla::FalseType; - using mozilla::TrueType; - #define integral_constant IntegralConstant - #define is_empty IsEmpty - #define false_type FalseType - #define true_type TrueType + using mozilla::Forward; + using mozilla::Move; + #define forward Forward + #define move Move - // If we have 'using mozilla::function', we're going to collide with - // 'std::function' on platforms that have it. Therefore we use a macro - // work around. - template - using Function = mozilla::function; - #define function Function + // If we have 'using mozilla::function', we're going to collide with + // 'std::function' on platforms that have it. Therefore we use a macro + // work around. + template + using moz_function = mozilla::function; + #define function moz_function + + typedef decltype(nullptr) moz_nullptr_t; + #define nullptr_t moz_nullptr_t + + using mozilla::DefaultDelete; + using mozilla::UniquePtr; + #define default_delete DefaultDelete + #define unique_ptr UniquePtr + + using mozilla::IsEnum; + using mozilla::IsIntegral; + using mozilla::FalseType; + using mozilla::TrueType; + #define is_enum IsEnum + #define is_integral IsIntegral + #define false_type FalseType + #define true_type TrueType + +#if SKIA_IMPLEMENTATION + using mozilla::IntegralConstant; + using mozilla::IsEmpty; + using mozilla::IsPod; + using mozilla::IsUnsigned; + #define integral_constant IntegralConstant + #define is_empty IsEmpty + #define is_pod IsPod + #define is_unsigned IsUnsigned #endif } @@ -75,12 +76,19 @@ template using bool_constant = mozilla::IntegralConstant; template using conditional_t = typename mozilla::Conditional::Type; template using enable_if_t = typename mozilla::EnableIf::Type; +template using is_convertible = mozilla::IsConvertible; +template using remove_pointer_t = typename mozilla::RemovePointer::Type; + +template struct underlying_type { + using type = __underlying_type(T); +}; +template using underlying_type_t = typename skstd::underlying_type::type; + } -#else /* !MOZ_SKIA */ +#else /* !MOZ_SKIA_AVOID_CXX11 */ #include -#include namespace skstd { @@ -103,7 +111,7 @@ template using remove_extent_t = typename std::remove_extent::ty // On all platforms, variadic functions only exist in the c calling convention. // mcvc 2013 introduced __vectorcall, but it wan't until 2015 that it was added to is_function. template struct is_function : std::false_type {}; -#if !defined(SK_BUILD_FOR_WIN) +#if !defined(WIN32) template struct is_function : std::true_type {}; #else template struct is_function : std::true_type {}; @@ -111,7 +119,7 @@ template struct is_function template struct is_function : std::true_type {}; template struct is_function : std::true_type {}; #endif -#if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 +#if defined(_MSC_VER) && (_M_IX86_FP >= 2 || defined(_M_AMD64) || defined(_M_X64)) template struct is_function : std::true_type {}; #endif #endif @@ -123,6 +131,29 @@ template using add_cv_t = typename std::add_cv::type; template using add_pointer_t = typename std::add_pointer::type; template using add_lvalue_reference_t = typename std::add_lvalue_reference::type; +template using common_type_t = typename std::common_type::type; + +// Chromium currently requires gcc 4.8.2 or a recent clang compiler, but uses libstdc++4.6.4. +// Note that Precise actually uses libstdc++4.6.3. +// Unfortunately, libstdc++ STL before libstdc++4.7 do not define std::underlying_type. +// Newer gcc and clang compilers have __underlying_type which does not depend on runtime support. +// See https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html for __GLIBCXX__ values. +// Unfortunately __GLIBCXX__ is a date, but no updates to versions before 4.7 are now anticipated. +#define SK_GLIBCXX_4_7_0 20120322 +// Updates to versions before 4.7 but released after 4.7 was released. +#define SK_GLIBCXX_4_5_4 20120702 +#define SK_GLIBCXX_4_6_4 20121127 +#if defined(__GLIBCXX__) && (__GLIBCXX__ < SK_GLIBCXX_4_7_0 || \ + __GLIBCXX__ == SK_GLIBCXX_4_5_4 || \ + __GLIBCXX__ == SK_GLIBCXX_4_6_4) +template struct underlying_type { + using type = __underlying_type(T); +}; +#else +template using underlying_type = std::underlying_type; +#endif +template using underlying_type_t = typename skstd::underlying_type::type; + template ::value || is_function::value || std::is_array::value> struct is_convertible_detector { @@ -182,7 +213,7 @@ template using same_cv_t = typename same_cv::type } // namespace sknonstd -#endif /* MOZ_SKIA */ +#endif /* MOZ_SKIA_AVOID_CXX11 */ // Just a pithier wrapper for enable_if_t. #define SK_WHEN(condition, T) skstd::enable_if_t diff --git a/gfx/skia/skia/include/private/SkTemplates.h b/gfx/skia/skia/include/private/SkTemplates.h index 81e23906e2..b006f4f942 100644 --- a/gfx/skia/skia/include/private/SkTemplates.h +++ b/gfx/skia/skia/include/private/SkTemplates.h @@ -13,8 +13,8 @@ #include "SkMath.h" #include "SkTLogic.h" #include "SkTypes.h" -#include "SkUniquePtr.h" #include +#include #include /** \file SkTemplates.h @@ -53,34 +53,32 @@ template struct SkFunctionWrapper { Call a function when this goes out of scope. The template uses two parameters, the object, and a function that is to be called in the destructor. - If detach() is called, the object reference is set to null. If the object + If release() is called, the object reference is set to null. If the object reference is null when the destructor is called, we do not call the function. */ template class SkAutoTCallVProc - : public skstd::unique_ptr> { + : public std::unique_ptr> { public: - SkAutoTCallVProc(T* obj): skstd::unique_ptr>(obj) {} + SkAutoTCallVProc(T* obj): std::unique_ptr>(obj) {} operator T*() const { return this->get(); } - T* detach() { return this->release(); } }; /** \class SkAutoTCallIProc Call a function when this goes out of scope. The template uses two parameters, the object, and a function that is to be called in the destructor. -If detach() is called, the object reference is set to null. If the object +If release() is called, the object reference is set to null. If the object reference is null when the destructor is called, we do not call the function. */ template class SkAutoTCallIProc - : public skstd::unique_ptr> { + : public std::unique_ptr> { public: - SkAutoTCallIProc(T* obj): skstd::unique_ptr>(obj) {} + SkAutoTCallIProc(T* obj): std::unique_ptr>(obj) {} operator T*() const { return this->get(); } - T* detach() { return this->release(); } }; /** \class SkAutoTDelete @@ -93,21 +91,21 @@ public: The size of a SkAutoTDelete is small: sizeof(SkAutoTDelete) == sizeof(T*) */ -template class SkAutoTDelete : public skstd::unique_ptr { +template class SkAutoTDelete : public std::unique_ptr { public: - SkAutoTDelete(T* obj = NULL) : skstd::unique_ptr(obj) {} + SkAutoTDelete(T* obj = NULL) : std::unique_ptr(obj) {} operator T*() const { return this->get(); } - void free() { this->reset(nullptr); } + +#if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) + // Need to update graphics/BitmapRegionDecoder.cpp. T* detach() { return this->release(); } +#endif }; -template class SkAutoTDeleteArray : public skstd::unique_ptr { +template class SkAutoTDeleteArray : public std::unique_ptr { public: - SkAutoTDeleteArray(T array[]) : skstd::unique_ptr(array) {} - - void free() { this->reset(nullptr); } - T* detach() { return this->release(); } + SkAutoTDeleteArray(T array[]) : std::unique_ptr(array) {} }; /** Allocate an array of T elements, and free the array in the destructor @@ -164,9 +162,9 @@ private: SkDEBUGCODE(int fCount;) }; -/** Wraps SkAutoTArray, with room for up to N elements preallocated +/** Wraps SkAutoTArray, with room for kCountRequested elements preallocated. */ -template class SkAutoSTArray : SkNoncopyable { +template class SkAutoSTArray : SkNoncopyable { public: /** Initialize with no objects */ SkAutoSTArray() { @@ -195,13 +193,13 @@ public: } if (fCount != count) { - if (fCount > N) { + if (fCount > kCount) { // 'fArray' was allocated last time so free it now SkASSERT((T*) fStorage != fArray); sk_free(fArray); } - if (count > N) { + if (count > kCount) { const uint64_t size64 = sk_64_mul(count, sizeof(T)); const size_t size = static_cast(size64); if (size != size64) { @@ -240,10 +238,21 @@ public: } private: +#if defined(GOOGLE3) + // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions + // have multiple large stack allocations. + static const int kMaxBytes = 4 * 1024; + static const int kCount = kCountRequested * sizeof(T) > kMaxBytes + ? kMaxBytes / sizeof(T) + : kCountRequested; +#else + static const int kCount = kCountRequested; +#endif + int fCount; T* fArray; // since we come right after fArray, fStorage should be properly aligned - char fStorage[N * sizeof(T)]; + char fStorage[kCount * sizeof(T)]; }; /** Manages an array of T elements, freeing the array in the destructor. @@ -271,9 +280,9 @@ public: } /** Resize the memory area pointed to by the current ptr without preserving contents. */ - T* reset(size_t count) { + T* reset(size_t count = 0) { sk_free(fPtr); - fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW); + fPtr = count ? (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW) : nullptr; return fPtr; } @@ -295,19 +304,12 @@ public: return fPtr[index]; } - /** - * Releases the block back to the heap - */ - void free() { - this->reset(0); - } - /** * Transfer ownership of the ptr to the caller, setting the internal * pointer to NULL. Note that this differs from get(), which also returns * the pointer, but it does not transfer ownership. */ - T* detach() { + T* release() { T* ptr = fPtr; fPtr = NULL; return ptr; @@ -317,12 +319,12 @@ private: T* fPtr; }; -template class SkAutoSTMalloc : SkNoncopyable { +template class SkAutoSTMalloc : SkNoncopyable { public: SkAutoSTMalloc() : fPtr(fTStorage) {} SkAutoSTMalloc(size_t count) { - if (count > N) { + if (count > kCount) { fPtr = (T*)sk_malloc_flags(count * sizeof(T), SK_MALLOC_THROW | SK_MALLOC_TEMP); } else { fPtr = fTStorage; @@ -340,7 +342,7 @@ public: if (fPtr != fTStorage) { sk_free(fPtr); } - if (count > N) { + if (count > kCount) { fPtr = (T*)sk_malloc_throw(count * sizeof(T)); } else { fPtr = fTStorage; @@ -368,10 +370,10 @@ public: // Reallocs the array, can be used to shrink the allocation. Makes no attempt to be intelligent void realloc(size_t count) { - if (count > N) { + if (count > kCount) { if (fPtr == fTStorage) { fPtr = (T*)sk_malloc_throw(count * sizeof(T)); - memcpy(fPtr, fTStorage, N * sizeof(T)); + memcpy(fPtr, fTStorage, kCount * sizeof(T)); } else { fPtr = (T*)sk_realloc_throw(fPtr, count * sizeof(T)); } @@ -381,9 +383,22 @@ public: } private: + // Since we use uint32_t storage, we might be able to get more elements for free. + static const size_t kCountWithPadding = SkAlign4(kCountRequested*sizeof(T)) / sizeof(T); +#if defined(GOOGLE3) + // Stack frame size is limited for GOOGLE3. 4k is less than the actual max, but some functions + // have multiple large stack allocations. + static const size_t kMaxBytes = 4 * 1024; + static const size_t kCount = kCountRequested * sizeof(T) > kMaxBytes + ? kMaxBytes / sizeof(T) + : kCountWithPadding; +#else + static const size_t kCount = kCountWithPadding; +#endif + T* fPtr; union { - uint32_t fStorage32[(N*sizeof(T) + 3) >> 2]; + uint32_t fStorage32[SkAlign4(kCount*sizeof(T)) >> 2]; T fTStorage[1]; // do NOT want to invoke T::T() }; }; diff --git a/gfx/skia/skia/include/private/SkUniquePtr.h b/gfx/skia/skia/include/private/SkUniquePtr.h deleted file mode 100644 index 474fc9ed42..0000000000 --- a/gfx/skia/skia/include/private/SkUniquePtr.h +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright 2015 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkUniquePtr_DEFINED -#define SkUniquePtr_DEFINED - -#include "SkTLogic.h" -#include -#include - -#ifdef MOZ_SKIA -#include "mozilla/UniquePtr.h" - -namespace std { - using mozilla::DefaultDelete; - using mozilla::UniquePtr; -} - -namespace skstd { - using mozilla::DefaultDelete; - using mozilla::UniquePtr; - #define default_delete DefaultDelete - #define unique_ptr UniquePtr -} -#else -namespace skstd { - -template struct default_delete { - /*constexpr*/ default_delete() /*noexcept*/ = default; - - template ::value>> - default_delete(const default_delete&) /*noexcept*/ {} - - void operator()(T* obj) const { - static_assert(sizeof(T) > 0, "Deleting pointer to incomplete type!"); - delete obj; - } -}; -template struct default_delete { - /*constexpr*/ default_delete() /*noexcept*/ = default; - - void operator()(T* obj) const { - static_assert(sizeof(T) > 0, "Deleting pointer to incomplete type!"); - delete [] obj; - } -}; - -template > class unique_ptr { - // remove_reference_t::pointer if that type exists, otherwise T*. - struct pointer_type_detector { - template static typename U::pointer detector(typename U::pointer*); - template static T* detector(...); - using type = decltype(detector>(0)); - }; - -public: - using pointer = typename pointer_type_detector::type; - using element_type = T; - using deleter_type = D; - -private: - template ::value /*&& !is_final::value*/> - struct compressed_base : private B { - /*constexpr*/ compressed_base() : B() {} - /*constexpr*/ compressed_base(const B& b) : B(b) {} - /*constexpr*/ compressed_base(B&& b) : B(std::move(b)) {} - /*constexpr*/ B& get() /*noexcept*/ { return *this; } - /*constexpr*/ B const& get() const /*noexcept*/ { return *this; } - void swap(compressed_base&) /*noexcept*/ { } - }; - - template struct compressed_base { - B fb; - /*constexpr*/ compressed_base() : B() {} - /*constexpr*/ compressed_base(const B& b) : fb(b) {} - /*constexpr*/ compressed_base(B&& b) : fb(std::move(b)) {} - /*constexpr*/ B& get() /*noexcept*/ { return fb; } - /*constexpr*/ B const& get() const /*noexcept*/ { return fb; } - void swap(compressed_base& that) /*noexcept*/ { SkTSwap(fb, that.fB); } - }; - - struct compressed_data : private compressed_base { - pointer fPtr; - /*constexpr*/ compressed_data() : compressed_base(), fPtr() {} - /*constexpr*/ compressed_data(const pointer& ptr, const deleter_type& d) - : compressed_base(d), fPtr(ptr) {} - template ::value && is_convertible::value - >> /*constexpr*/ compressed_data(U1&& ptr, U2&& d) - : compressed_base(std::forward(d)), fPtr(std::forward(ptr)) {} - /*constexpr*/ pointer& getPointer() /*noexcept*/ { return fPtr; } - /*constexpr*/ pointer const& getPointer() const /*noexcept*/ { return fPtr; } - /*constexpr*/ deleter_type& getDeleter() /*noexcept*/ { - return compressed_base::get(); - } - /*constexpr*/ deleter_type const& getDeleter() const /*noexcept*/ { - return compressed_base::get(); - } - void swap(compressed_data& that) /*noexcept*/ { - compressed_base::swap(static_cast>(that)); - SkTSwap(fPtr, that.fPtr); - } - }; - compressed_data data; - -public: - /*constexpr*/ unique_ptr() /*noexcept*/ : data() { - static_assert(!std::is_pointer::value, "Deleter nullptr function pointer!"); - } - - /*constexpr*/ unique_ptr(std::nullptr_t) /*noexcept*/ : unique_ptr() { } - - explicit unique_ptr(pointer ptr) /*noexcept*/ : data(ptr, deleter_type()) { - static_assert(!std::is_pointer::value, "Deleter nullptr function pointer!"); - } - - unique_ptr(pointer ptr, - conditional_t::value, - deleter_type, const deleter_type&> d) - /*noexcept*/ : data(ptr, d) - {} - - unique_ptr(pointer ptr, remove_reference_t&& d) /*noexcept*/ - : data(std::move(ptr), std::move(d)) - { - static_assert(!std::is_reference::value, - "Binding an rvalue reference deleter as an lvalue reference deleter is not allowed."); - } - - - unique_ptr(unique_ptr&& that) /*noexcept*/ - : data(that.release(), std::forward(that.get_deleter())) - {} - - template ::pointer, pointer>::value && - !std::is_array::value && - conditional_t::value, - std::is_same, - is_convertible>::value>> - unique_ptr(unique_ptr&& that) /*noexcept*/ - : data(that.release(), std::forward(that.get_deleter())) - {} - - ~unique_ptr() /*noexcept*/ { - pointer& ptr = data.getPointer(); - if (ptr != nullptr) { - get_deleter()(ptr); - } - ptr = pointer(); - } - - unique_ptr& operator=(unique_ptr&& that) /*noexcept*/ { - reset(that.release()); - get_deleter() = std::forward(that.get_deleter()); - return *this; - } - - template enable_if_t< - is_convertible::pointer, pointer>::value && - !std::is_array::value, - unique_ptr&> operator=(unique_ptr&& that) /*noexcept*/ { - reset(that.release()); - get_deleter() = std::forward(that.get_deleter()); - return *this; - } - - unique_ptr& operator=(std::nullptr_t) /*noexcept*/ { - reset(); - return *this; - } - - add_lvalue_reference_t operator*() const { - SkASSERT(get() != pointer()); - return *get(); - } - - pointer operator->() const /*noexcept*/ { - SkASSERT(get() != pointer()); - return get(); - } - - pointer get() const /*noexcept*/ { - return data.getPointer(); - } - - deleter_type& get_deleter() /*noexcept*/ { - return data.getDeleter(); - } - - const deleter_type& get_deleter() const /*noexcept*/ { - return data.getDeleter(); - } - - //explicit operator bool() const noexcept { - bool is_attached() const /*noexcept*/ { - return get() == pointer() ? false : true; - } - - pointer release() /*noexcept*/ { - pointer ptr = get(); - data.getPointer() = pointer(); - return ptr; - } - - void reset(pointer ptr = pointer()) /*noexcept*/ { - SkTSwap(data.getPointer(), ptr); - if (ptr != pointer()) { - get_deleter()(ptr); - } - } - - void swap(unique_ptr& that) /*noexcept*/ { - SkTSwap(data, that.data); - } - - unique_ptr(const unique_ptr&) = delete; - unique_ptr& operator=(const unique_ptr&) = delete; -}; - -template class unique_ptr { - // remove_reference_t::pointer if that type exists, otherwise T*. - struct pointer_type_detector { - template static typename U::pointer detector(typename U::pointer*); - template static T* detector(...); - using type = decltype(detector>(0)); - }; - -public: - using pointer = typename pointer_type_detector::type; - using element_type = T; - using deleter_type = D; - -private: - template ::value /*&& !is_final::value*/> - struct compressed_base : private B { - /*constexpr*/ compressed_base() : B() {} - /*constexpr*/ compressed_base(const B& b) : B(b) {} - /*constexpr*/ compressed_base(B&& b) : B(std::move(b)) {} - /*constexpr*/ B& get() /*noexcept*/ { return *this; } - /*constexpr*/ B const& get() const /*noexcept*/ { return *this; } - void swap(compressed_base&) /*noexcept*/ { } - }; - - template struct compressed_base { - B fb; - /*constexpr*/ compressed_base() : B() {} - /*constexpr*/ compressed_base(const B& b) : fb(b) {} - /*constexpr*/ compressed_base(B&& b) : fb(std::move(b)) {} - /*constexpr*/ B& get() /*noexcept*/ { return fb; } - /*constexpr*/ B const& get() const /*noexcept*/ { return fb; } - void swap(compressed_base& that) /*noexcept*/ { SkTSwap(fb, that.fB); } - }; - - struct compressed_data : private compressed_base { - pointer fPtr; - /*constexpr*/ compressed_data() : compressed_base(), fPtr() {} - /*constexpr*/ compressed_data(const pointer& ptr, const deleter_type& d) - : compressed_base(d), fPtr(ptr) {} - template ::value && is_convertible::value - >> /*constexpr*/ compressed_data(U1&& ptr, U2&& d) - : compressed_base(std::forward(d)), fPtr(std::forward(ptr)) {} - /*constexpr*/ pointer& getPointer() /*noexcept*/ { return fPtr; } - /*constexpr*/ pointer const& getPointer() const /*noexcept*/ { return fPtr; } - /*constexpr*/ deleter_type& getDeleter() /*noexcept*/ { - return compressed_base::get(); - } - /*constexpr*/ deleter_type const& getDeleter() const /*noexcept*/ { - return compressed_base::get(); - } - void swap(compressed_data& that) /*noexcept*/ { - compressed_base::swap(static_cast>(that)); - SkTSwap(fPtr, that.fPtr); - } - }; - compressed_data data; - -public: - /*constexpr*/ unique_ptr() /*noexcept*/ : data() { - static_assert(!std::is_pointer::value, "Deleter nullptr function pointer!"); - } - - /*constexpr*/ unique_ptr(std::nullptr_t) /*noexcept*/ : unique_ptr() { } - - explicit unique_ptr(pointer ptr) /*noexcept*/ : data(ptr, deleter_type()) { - static_assert(!std::is_pointer::value, "Deleter nullptr function pointer!"); - } - - unique_ptr(pointer ptr, - conditional_t::value, - deleter_type, const deleter_type&> d) - /*noexcept*/ : data(ptr, d) - {} - - unique_ptr(pointer ptr, remove_reference_t&& d) /*noexcept*/ - : data(std::move(ptr), std::move(d)) - { - static_assert(!std::is_reference::value, - "Binding an rvalue reference deleter as an lvalue reference deleter is not allowed."); - } - - unique_ptr(unique_ptr&& that) /*noexcept*/ - : data(that.release(), std::forward(that.get_deleter())) - {} - - ~unique_ptr() { - pointer& ptr = data.getPointer(); - if (ptr != nullptr) { - get_deleter()(ptr); - } - ptr = pointer(); - } - - unique_ptr& operator=(unique_ptr&& that) /*noexcept*/ { - reset(that.release()); - get_deleter() = std::forward(that.get_deleter()); - return *this; - } - - unique_ptr& operator=(std::nullptr_t) /*noexcept*/ { - reset(); - return *this; - } - - add_lvalue_reference_t operator[](size_t i) const { - SkASSERT(get() != pointer()); - return get()[i]; - } - - pointer get() const /*noexcept*/ { - return data.getPointer(); - } - - deleter_type& get_deleter() /*noexcept*/ { - return data.getDeleter(); - } - - const deleter_type& get_deleter() const /*noexcept*/ { - return data.getDeleter(); - } - - //explicit operator bool() const noexcept { - bool is_attached() const /*noexcept*/ { - return get() == pointer() ? false : true; - } - - pointer release() /*noexcept*/ { - pointer ptr = get(); - data.getPointer() = pointer(); - return ptr; - } - - void reset(pointer ptr = pointer()) /*noexcept*/ { - SkTSwap(data.getPointer(), ptr); - if (ptr != pointer()) { - get_deleter()(ptr); - } - } - - template void reset(U*) = delete; - - void swap(unique_ptr& that) /*noexcept*/ { - data.swap(that.data); - } - - unique_ptr(const unique_ptr&) = delete; - unique_ptr& operator=(const unique_ptr&) = delete; -}; - -template -inline void swap(unique_ptr& a, unique_ptr& b) /*noexcept*/ { - a.swap(b); -} - -template -inline bool operator==(const unique_ptr& a, const unique_ptr& b) { - return a.get() == b.get(); -} - -template -inline bool operator==(const unique_ptr& a, std::nullptr_t) /*noexcept*/ { - //return !a; - return !a.is_attached(); -} - -template -inline bool operator==(std::nullptr_t, const unique_ptr& b) /*noexcept*/ { - //return !b; - return !b.is_attached(); -} - -template -inline bool operator!=(const unique_ptr& a, const unique_ptr& b) { - return a.get() != b.get(); -} - -template -inline bool operator!=(const unique_ptr& a, std::nullptr_t) /*noexcept*/ { - //return (bool)a; - return a.is_attached(); -} - -template -inline bool operator!=(std::nullptr_t, const unique_ptr& b) /*noexcept*/ { - //return (bool)b; - return b.is_attached(); -} - -} // namespace skstd -#endif - -#endif diff --git a/gfx/skia/skia/include/utils/SkDebugUtils.h b/gfx/skia/skia/include/utils/SkDebugUtils.h deleted file mode 100644 index 00ed41fecd..0000000000 --- a/gfx/skia/skia/include/utils/SkDebugUtils.h +++ /dev/null @@ -1,94 +0,0 @@ - -/* - * Copyright 2013 Google, Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkDebugUtils_DEFINED -#define SkDebugUtils_DEFINED - -#include "SkTypes.h" - -// These functions dump 0, 1, and 2d arrays of data in a format that's -// compatible with Mathematica for quick visualization - - -template -inline void SkDebugDumpMathematica( const T val ) { - SkDEBUGFAIL("Need to specialize SkDebugDumpMathematica for your type, sorry."); -} - -template -inline void SkDebugDumpMathematica(const char *name, const T *array, int size) { - SkDebugf("%s", name); - SkDebugf(" = {"); - for (int i=0 ; i < size ; i++) { - SkDebugDumpMathematica(array[i]); - if (i != size-1) SkDebugf(", "); - } - SkDebugf("};\n"); -} - -template -inline void SkDebugDumpMathematica(const char *name, const T *array, int width, int height) { - SkDebugf("%s", name); - SkDebugf(" = {\n"); - for (int i=0 ; i < height ; i++) { - SkDebugf(" {"); - for (int j = 0 ; j < width ; j++) { - SkDebugDumpMathematica(array[i*width + j]); - if (j != width-1) { - SkDebugf(", "); - } - } - SkDebugf("}"); - if (i != height-1) { - SkDebugf(", \n"); - } - } - SkDebugf("\n};\n"); -} - -template -inline void SkDebugDumpMathematica( const char *name, const T val ) { - SkDebugf("%s", name); - SkDebugf(" = "); - SkDebugDumpMathematica(val); - SkDebugf(";\n"); -} - -template<> -inline void SkDebugDumpMathematica( const uint8_t val ) { - SkDebugf("%u", val); -} - -template<> -inline void SkDebugDumpMathematica( const unsigned int val ) { - SkDebugf("%u", val); -} - -template<> -inline void SkDebugDumpMathematica( const int val ) { - SkDebugf("%d", val); -} - -template<> -inline void SkDebugDumpMathematica( const size_t val ) { - SkDebugf("%u", val); -} - -template<> -inline void SkDebugDumpMathematica( const char * val ) { - SkDebugf("%s", val); -} - -template<> -inline void SkDebugDumpMathematica( float val ) { - SkDebugf("%f", val); -} - - -#endif diff --git a/gfx/skia/skia/include/utils/SkDumpCanvas.h b/gfx/skia/skia/include/utils/SkDumpCanvas.h index d4c6dbf81b..1c16bf3f03 100644 --- a/gfx/skia/skia/include/utils/SkDumpCanvas.h +++ b/gfx/skia/skia/include/utils/SkDumpCanvas.h @@ -48,6 +48,7 @@ public: kDrawVertices_Verb, kDrawPatch_Verb, kDrawData_Verb, // obsolete + kDrawAnnotation_Verb, kCull_Verb }; @@ -120,6 +121,7 @@ protected: void onClipRegion(const SkRegion&, SkRegion::Op) override; void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; + void onDrawAnnotation(const SkRect&, const char key[], SkData* value) override; static const char* EdgeStyleToAAString(ClipEdgeStyle edgeStyle); diff --git a/gfx/skia/skia/include/utils/SkLayer.h b/gfx/skia/skia/include/utils/SkLayer.h index 7fdc6b2208..25bc32886e 100644 --- a/gfx/skia/skia/include/utils/SkLayer.h +++ b/gfx/skia/skia/include/utils/SkLayer.h @@ -10,8 +10,8 @@ #ifndef SkLayer_DEFINED #define SkLayer_DEFINED +#include "../private/SkTDArray.h" #include "SkRefCnt.h" -#include "SkTDArray.h" #include "SkColor.h" #include "SkMatrix.h" #include "SkPoint.h" diff --git a/gfx/skia/skia/include/utils/SkNWayCanvas.h b/gfx/skia/skia/include/utils/SkNWayCanvas.h index 8d824d7e2c..f2a99db802 100644 --- a/gfx/skia/skia/include/utils/SkNWayCanvas.h +++ b/gfx/skia/skia/include/utils/SkNWayCanvas.h @@ -9,8 +9,8 @@ #ifndef SkNWayCanvas_DEFINED #define SkNWayCanvas_DEFINED +#include "../private/SkTDArray.h" #include "SkCanvas.h" -#include "SkTDArray.h" class SK_API SkNWayCanvas : public SkCanvas { public: @@ -24,7 +24,9 @@ public: /////////////////////////////////////////////////////////////////////////// // These are forwarded to the N canvases we're referencing +#ifdef SK_SUPPORT_LEGACY_DRAWFILTER SkDrawFilter* setDrawFilter(SkDrawFilter*) override; +#endif protected: SkTDArray fList; @@ -77,6 +79,7 @@ protected: void onClipRegion(const SkRegion&, SkRegion::Op) override; void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; + void onDrawAnnotation(const SkRect&, const char[], SkData*) override; class Iter; diff --git a/gfx/skia/skia/include/utils/SkRTConf.h b/gfx/skia/skia/include/utils/SkRTConf.h index 6c7963c64e..c3e97ef5e3 100644 --- a/gfx/skia/skia/include/utils/SkRTConf.h +++ b/gfx/skia/skia/include/utils/SkRTConf.h @@ -9,10 +9,10 @@ #ifndef SkRTConf_DEFINED #define SkRTConf_DEFINED -#include "SkTArray.h" +#include "../private/SkTDArray.h" +#include "../private/SkTDict.h" #include "SkString.h" #include "SkStream.h" -#include "../private/SkTDict.h" /** \class SkRTConfBase Non-templated base class for the runtime configs diff --git a/gfx/skia/skia/include/utils/SkRandom.h b/gfx/skia/skia/include/utils/SkRandom.h index aee2d7a0aa..7b5663118c 100644 --- a/gfx/skia/skia/include/utils/SkRandom.h +++ b/gfx/skia/skia/include/utils/SkRandom.h @@ -8,6 +8,7 @@ #ifndef SkRandom_DEFINED #define SkRandom_DEFINED +#include "../private/SkFixed.h" #include "SkScalar.h" /** \class SkRandom @@ -97,16 +98,6 @@ public: return this->nextRangeU(0, count - 1); } - /** Return the next pseudo random number expressed as an unsigned SkFixed - in the range [0..SK_Fixed1). - */ - SkFixed nextUFixed1() { return this->nextU() >> 16; } - - /** Return the next pseudo random number expressed as a signed SkFixed - in the range (-SK_Fixed1..SK_Fixed1). - */ - SkFixed nextSFixed1() { return this->nextS() >> 15; } - /** Return the next pseudo random number expressed as a SkScalar in the range [0..SK_Scalar1). */ @@ -120,7 +111,7 @@ public: } /** Return the next pseudo random number expressed as a SkScalar - in the range (-SK_Scalar1..SK_Scalar1). + in the range [-SK_Scalar1..SK_Scalar1). */ SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); } @@ -164,6 +155,16 @@ private: } static uint32_t NextLCG(uint32_t seed) { return kMul*seed + kAdd; } + /** Return the next pseudo random number expressed as an unsigned SkFixed + in the range [0..SK_Fixed1). + */ + SkFixed nextUFixed1() { return this->nextU() >> 16; } + + /** Return the next pseudo random number expressed as a signed SkFixed + in the range [-SK_Fixed1..SK_Fixed1). + */ + SkFixed nextSFixed1() { return this->nextS() >> 15; } + // See "Numerical Recipes in C", 1992 page 284 for these constants // For the LCG that sets the initial state from a seed enum { diff --git a/gfx/skia/skia/include/views/SkBGViewArtist.h b/gfx/skia/skia/include/views/SkBGViewArtist.h deleted file mode 100644 index 33054c8038..0000000000 --- a/gfx/skia/skia/include/views/SkBGViewArtist.h +++ /dev/null @@ -1,33 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkBGViewArtist_DEFINED -#define SkBGViewArtist_DEFINED - -#include "SkView.h" -#include "SkPaint.h" - -class SkBGViewArtist : public SkView::Artist { -public: - SkBGViewArtist(SkColor c = SK_ColorWHITE); - virtual ~SkBGViewArtist(); - - const SkPaint& paint() const { return fPaint; } - SkPaint& paint() { return fPaint; } - -protected: - // overrides - virtual void onDraw(SkView*, SkCanvas*); - virtual void onInflate(const SkDOM&, const SkDOM::Node*); - -private: - SkPaint fPaint; -}; - -#endif diff --git a/gfx/skia/skia/include/views/SkEvent.h b/gfx/skia/skia/include/views/SkEvent.h index 0af76fe686..b8fc00ef51 100644 --- a/gfx/skia/skia/include/views/SkEvent.h +++ b/gfx/skia/skia/include/views/SkEvent.h @@ -206,13 +206,20 @@ public: /** * Post to the event queue using the event's targetID or target-proc. * The event will be delivered no sooner than the specified millisecond - * time, as measured by SkTime::GetMSecs(). + * time, as measured by GetMSecsSinceStartup(). * * The event must be dynamically allocated, as ownership is transferred to * the event queue. It cannot be allocated on the stack or in a global. */ void postTime(SkMSec time); + /** + * Returns ~zero the first time it's called, then returns the number of + * milliseconds since the first call. Behavior is undefined if the program + * runs more than ~25 days. + */ + static SkMSec GetMSecsSinceStartup(); + /////////////////////////////////////////////// /** Porting layer must call these functions **/ /////////////////////////////////////////////// diff --git a/gfx/skia/skia/include/views/SkOSMenu.h b/gfx/skia/skia/include/views/SkOSMenu.h index 73254187ef..913b915387 100644 --- a/gfx/skia/skia/include/views/SkOSMenu.h +++ b/gfx/skia/skia/include/views/SkOSMenu.h @@ -10,8 +10,8 @@ #ifndef SkOSMenu_DEFINED #define SkOSMenu_DEFINED +#include "../private/SkTDArray.h" #include "SkEvent.h" -#include "SkTDArray.h" class SkOSMenu { public: diff --git a/gfx/skia/skia/include/views/SkOSWindow_Android.h b/gfx/skia/skia/include/views/SkOSWindow_Android.h index 09163fc1cb..74175bafd1 100644 --- a/gfx/skia/skia/include/views/SkOSWindow_Android.h +++ b/gfx/skia/skia/include/views/SkOSWindow_Android.h @@ -29,15 +29,13 @@ public: }; bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo* info); - void detach(); + void release(); void present(); bool makeFullscreen() { return true; } void closeWindow(); void setVsync(bool); bool destroyRequested() { return fDestroyRequested; } - virtual void onPDFSaved(const char title[], const char desc[], const char path[]); - protected: // overrides from SkWindow virtual void onHandleInval(const SkIRect&); diff --git a/gfx/skia/skia/include/views/SkOSWindow_Mac.h b/gfx/skia/skia/include/views/SkOSWindow_Mac.h index 6ce898321f..efa97bf872 100644 --- a/gfx/skia/skia/include/views/SkOSWindow_Mac.h +++ b/gfx/skia/skia/include/views/SkOSWindow_Mac.h @@ -32,7 +32,7 @@ public: #endif // SK_COMMAND_BUFFER }; - void detach(); + void release(); bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*); void present(); diff --git a/gfx/skia/skia/include/views/SkOSWindow_SDL.h b/gfx/skia/skia/include/views/SkOSWindow_SDL.h index e08108add4..6823441715 100644 --- a/gfx/skia/skia/include/views/SkOSWindow_SDL.h +++ b/gfx/skia/skia/include/views/SkOSWindow_SDL.h @@ -28,7 +28,7 @@ public: #endif // SK_COMMAND_BUFFER }; - void detach(); + void release(); bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*); void present(); bool makeFullscreen(); diff --git a/gfx/skia/skia/include/views/SkOSWindow_Unix.h b/gfx/skia/skia/include/views/SkOSWindow_Unix.h index ecd0a14521..30386da183 100644 --- a/gfx/skia/skia/include/views/SkOSWindow_Unix.h +++ b/gfx/skia/skia/include/views/SkOSWindow_Unix.h @@ -45,7 +45,7 @@ public: }; bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*); - void detach(); + void release(); void present(); int getMSAASampleCount() const { return fMSAASampleCount; } diff --git a/gfx/skia/skia/include/views/SkOSWindow_Win.h b/gfx/skia/skia/include/views/SkOSWindow_Win.h index c1a68c621e..eb8b1fc892 100644 --- a/gfx/skia/skia/include/views/SkOSWindow_Win.h +++ b/gfx/skia/skia/include/views/SkOSWindow_Win.h @@ -48,7 +48,7 @@ public: }; bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*); - void detach(); + void release(); void present(); bool wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); @@ -98,10 +98,11 @@ private: #if SK_SUPPORT_GPU void* fHGLRC; #if SK_ANGLE - EGLDisplay fDisplay; - EGLContext fContext; - EGLSurface fSurface; - EGLConfig fConfig; + EGLDisplay fDisplay; + EGLContext fContext; + EGLSurface fSurface; + EGLConfig fConfig; + SkAutoTUnref fANGLEInterface; #endif // SK_ANGLE #if SK_COMMAND_BUFFER SkCommandBufferGLContext* fCommandBuffer; diff --git a/gfx/skia/skia/include/views/SkOSWindow_iOS.h b/gfx/skia/skia/include/views/SkOSWindow_iOS.h index 071d6db18c..5a8b2e3d2f 100644 --- a/gfx/skia/skia/include/views/SkOSWindow_iOS.h +++ b/gfx/skia/skia/include/views/SkOSWindow_iOS.h @@ -21,7 +21,7 @@ public: kNativeGL_BackEndType, }; - void detach(); + void release(); bool attach(SkBackEndTypes attachType, int msaaSampleCount, AttachmentInfo*); void present(); diff --git a/gfx/skia/skia/include/views/SkParsePaint.h b/gfx/skia/skia/include/views/SkParsePaint.h deleted file mode 100644 index 066292f9c7..0000000000 --- a/gfx/skia/skia/include/views/SkParsePaint.h +++ /dev/null @@ -1,26 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkParsePaint_DEFINED -#define SkParsePaint_DEFINED - -#include "SkPaint.h" -#include "SkDOM.h" - -/** "color" color - "opacity" scalar [0..1] - "stroke-width" scalar (0...inf) - "text-size" scalar (0..inf) - "is-stroke" bool - "is-antialias" bool - "is-lineartext" bool -*/ -void SkPaint_Inflate(SkPaint*, const SkDOM&, const SkDOM::Node*); - -#endif diff --git a/gfx/skia/skia/include/views/SkStackViewLayout.h b/gfx/skia/skia/include/views/SkStackViewLayout.h deleted file mode 100644 index f2a7cf04c7..0000000000 --- a/gfx/skia/skia/include/views/SkStackViewLayout.h +++ /dev/null @@ -1,88 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkStackViewLayout_DEFINED -#define SkStackViewLayout_DEFINED - -#include "SkView.h" - -class SkStackViewLayout : public SkView::Layout { -public: - SkStackViewLayout(); - - enum Orient { - kHorizontal_Orient, - kVertical_Orient, - - kOrientCount - }; - Orient getOrient() const { return (Orient)fOrient; } - void setOrient(Orient); - - void getMargin(SkRect*) const; - void setMargin(const SkRect&); - - SkScalar getSpacer() const { return fSpacer; } - void setSpacer(SkScalar); - - /** Controls the posititioning in the same direction as the orientation - */ - enum Pack { - kStart_Pack, - kCenter_Pack, - kEnd_Pack, - - kPackCount - }; - Pack getPack() const { return (Pack)fPack; } - void setPack(Pack); - - /** Controls the posititioning at right angles to the orientation - */ - enum Align { - kStart_Align, - kCenter_Align, - kEnd_Align, - kStretch_Align, - - kAlignCount - }; - Align getAlign() const { return (Align)fAlign; } - void setAlign(Align); - - bool getRound() const { return SkToBool(fRound); } - void setRound(bool); - -protected: - virtual void onLayoutChildren(SkView* parent); - virtual void onInflate(const SkDOM&, const SkDOM::Node*); - -private: - SkRect fMargin; - SkScalar fSpacer; - uint8_t fOrient, fPack, fAlign, fRound; -}; - -class SkFillViewLayout : public SkView::Layout { -public: - SkFillViewLayout(); - void getMargin(SkRect*) const; - void setMargin(const SkRect&); - -protected: - // overrides; - virtual void onLayoutChildren(SkView* parent); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - -private: - SkRect fMargin; - typedef SkView::Layout INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/include/views/SkTouchGesture.h b/gfx/skia/skia/include/views/SkTouchGesture.h index 3a0cfce18d..60487c7a2f 100644 --- a/gfx/skia/skia/include/views/SkTouchGesture.h +++ b/gfx/skia/skia/include/views/SkTouchGesture.h @@ -8,7 +8,7 @@ #ifndef SkTouchGesture_DEFINED #define SkTouchGesture_DEFINED -#include "SkTDArray.h" +#include "../private/SkTDArray.h" #include "SkMatrix.h" struct SkFlingState { @@ -55,14 +55,14 @@ private: float fStartX, fStartY; float fPrevX, fPrevY; float fLastX, fLastY; - SkMSec fPrevT, fLastT; + float fPrevT, fLastT; }; SkTDArray fTouches; State fState; SkMatrix fLocalM, fGlobalM; SkFlingState fFlinger; - SkMSec fLastUpT; + double fLastUpMillis; SkPoint fLastUpP; diff --git a/gfx/skia/skia/include/views/SkViewInflate.h b/gfx/skia/skia/include/views/SkViewInflate.h deleted file mode 100644 index 4ec36a67df..0000000000 --- a/gfx/skia/skia/include/views/SkViewInflate.h +++ /dev/null @@ -1,71 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkViewInflate_DEFINED -#define SkViewInflate_DEFINED - -#include "SkDOM.h" -#include "../private/SkTDict.h" -#include "SkEvent.h" - -class SkView; - -class SkViewInflate { -public: - SkViewInflate(); - virtual ~SkViewInflate(); - - /** Return the tree of inflated views. If root is null, create the root element - as a view, otherwise assume root is that view, and just "inflate" it. - - Returns null if the tree cannot be built. - */ - SkView* inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root = NULL); - SkView* inflate(const char xml[], size_t len, SkView* root = NULL); - - /** Given an id attribute value, return the corresponding view, or null - if no match is found. - */ - SkView* findViewByID(const char id[]) const; - - SkDEBUGCODE(void dump() const;) - -protected: - /* Override this in your subclass to handle instantiating views - Call the inherited version for nodes you don't recognize. - - Do not call "inflate" on the view, just return it. This will - get called automatically after createView returns. - */ - virtual SkView* createView(const SkDOM& dom, const SkDOM::Node* node); - /** Base implementation calls view->inflate(dom, node). Subclasses may override this - to perform additional initializations to view, either before or after calling - the inherited version. - */ - virtual void inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node); - -private: - enum { - kMinIDStrAlloc = 64 - }; - SkTDict fIDs; - - struct IDStr { - SkView* fView; - char* fStr; - }; - SkTDArray fListenTo, fBroadcastTo; - SkChunkAlloc fStrings; - - void addIDStr(SkTDArray* list, SkView*, const char* str); - - void rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent); -}; - -#endif diff --git a/gfx/skia/skia/include/views/SkWidget.h b/gfx/skia/skia/include/views/SkWidget.h deleted file mode 100644 index 3c9363440b..0000000000 --- a/gfx/skia/skia/include/views/SkWidget.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkWidget_DEFINED -#define SkWidget_DEFINED - -#include "SkBitmap.h" -#include "SkDOM.h" -#include "SkPaint.h" -#include "SkString.h" -#include "SkTDArray.h" -#include "SkTextBox.h" -#include "SkView.h" - -class SkEvent; -class SkInterpolator; -class SkShader; - -//////////////////////////////////////////////////////////////////////////////// - -class SkWidget : public SkView { -public: - SkWidget(uint32_t flags = 0) : SkView(flags | kFocusable_Mask | kEnabled_Mask) {} - - /** Call this to post the widget's event to its listeners */ - void postWidgetEvent(); - - static void Init(); - static void Term(); -protected: - // override to add slots to an event before posting - virtual void prepareWidgetEvent(SkEvent*); - virtual void onEnabledChange(); - - // to initialize the event from XML - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - -private: - SkEvent fEvent; - typedef SkView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkHasLabelWidget : public SkWidget { -public: - SkHasLabelWidget(uint32_t flags = 0) : SkWidget(flags) {} - - size_t getLabel(SkString* label = NULL) const; - size_t getLabel(char lable[] = NULL) const; - void setLabel(const SkString&); - void setLabel(const char label[]); - void setLabel(const char label[], size_t len); - -protected: - // called when the label changes - virtual void onLabelChange(); - - // overrides - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); - -private: - SkString fLabel; - typedef SkWidget INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkButtonWidget : public SkHasLabelWidget { -public: - SkButtonWidget(uint32_t flags = 0) : SkHasLabelWidget(flags), fState(kOff_State) {} - - enum State { - kOff_State, //!< XML: buttonState="off" - kOn_State, //!< XML: buttonState="on" - kUnknown_State //!< XML: buttonState="unknown" - }; - State getButtonState() const { return fState; } - void setButtonState(State); - -protected: - /** called when the label changes. default behavior is to inval the widget */ - virtual void onButtonStateChange(); - - // overrides - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); - -private: - State fState; - typedef SkHasLabelWidget INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkPushButtonWidget : public SkButtonWidget { -public: - SkPushButtonWidget(uint32_t flags = 0) : SkButtonWidget(flags) {} - -protected: - bool onEvent(const SkEvent&) override; - void onDraw(SkCanvas*) override; - Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override; - bool onClick(Click* click) override; - -private: - typedef SkButtonWidget INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkCheckBoxWidget : public SkButtonWidget { -public: - SkCheckBoxWidget(uint32_t flags = 0); - -protected: - virtual bool onEvent(const SkEvent&); - virtual void onDraw(SkCanvas*); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); - -private: - typedef SkButtonWidget INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkStaticTextView : public SkView { -public: - SkStaticTextView(uint32_t flags = 0); - virtual ~SkStaticTextView(); - - enum Mode { - kFixedSize_Mode, - kAutoWidth_Mode, - kAutoHeight_Mode, - - kModeCount - }; - Mode getMode() const { return (Mode)fMode; } - void setMode(Mode); - - SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; } - void setSpacingAlign(SkTextBox::SpacingAlign); - - void getMargin(SkPoint* margin) const; - void setMargin(SkScalar dx, SkScalar dy); - - size_t getText(SkString* text = NULL) const; - size_t getText(char text[] = NULL) const; - void setText(const SkString&); - void setText(const char text[]); - void setText(const char text[], size_t len); - - void getPaint(SkPaint*) const; - void setPaint(const SkPaint&); - -protected: - // overrides - virtual void onDraw(SkCanvas*); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); - -private: - SkPoint fMargin; - SkString fText; - SkPaint fPaint; - uint8_t fMode; - uint8_t fSpacingAlign; - - void computeSize(); - - typedef SkView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkBitmapView : public SkView { -public: - SkBitmapView(uint32_t flags = 0); - virtual ~SkBitmapView(); - - bool getBitmap(SkBitmap*) const; - void setBitmap(const SkBitmap*, bool viewOwnsPixels); - bool loadBitmapFromFile(const char path[]); - -protected: - virtual void onDraw(SkCanvas*); - virtual void onInflate(const SkDOM&, const SkDOM::Node*); - -private: - SkBitmap fBitmap; - typedef SkView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkHasLabelView : public SkView { -public: - void getLabel(SkString*) const; - void setLabel(const SkString&); - void setLabel(const char label[]); - -protected: - SkString fLabel; - - // called when the label changes - virtual void onLabelChange(); - - // overrides - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkPushButtonView : public SkHasLabelView { -public: - SkPushButtonView(uint32_t flags = 0); - -protected: - virtual void onDraw(SkCanvas*); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkCheckBoxView : public SkHasLabelView { -public: - SkCheckBoxView(uint32_t flags = 0); - - enum State { - kOff_State, - kOn_State, - kMaybe_State - }; - State getState() const { return fState; } - void setState(State); - -protected: - virtual void onDraw(SkCanvas*); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); - -private: - State fState; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkProgressView : public SkView { -public: - SkProgressView(uint32_t flags = 0); - virtual ~SkProgressView(); - - uint16_t getValue() const { return fValue; } - uint16_t getMax() const { return fMax; } - - void setMax(U16CPU max); - void setValue(U16CPU value); - -protected: - virtual void onDraw(SkCanvas*); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - -private: - uint16_t fValue, fMax; - SkShader* fOnShader, *fOffShader; - SkInterpolator* fInterp; - bool fDoInterp; - - typedef SkView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkListSource : public SkEventSink { -public: - virtual int countRows() = 0; - virtual void getRow(int index, SkString* left, SkString* right) = 0; - virtual SkEvent* getEvent(int index); - - static SkListSource* CreateFromDir(const char path[], const char suffix[], - const char targetPrefix[]); - static SkListSource* CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node); -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkListView : public SkView { -public: - SkListView(uint32_t flags = 0); - virtual ~SkListView(); - - SkScalar getRowHeight() const { return fRowHeight; } - void setRowHeight(SkScalar); - - /** Return the index of the selected row, or -1 if none - */ - int getSelection() const { return fCurrIndex; } - /** Set the index of the selected row, or -1 for none - */ - void setSelection(int); - - void moveSelectionUp(); - void moveSelectionDown(); - - enum Attr { - kBG_Attr, - kNormalText_Attr, - kHiliteText_Attr, - kHiliteCell_Attr, - kAttrCount - }; - SkPaint& paint(Attr); - - SkListSource* getListSource() const { return fSource; } - SkListSource* setListSource(SkListSource*); - -#if 0 - enum Action { - kSelectionChange_Action, - kSelectionPicked_Action, - kActionCount - }; - /** If event is not null, it is retained by the view, and a copy - of the event will be posted to its listeners when the specified - action occurs. If event is null, then no event will be posted for - the specified action. - */ - void setActionEvent(Action, SkEvent* event); -#endif - -protected: - virtual void onDraw(SkCanvas*); - virtual void onSizeChange(); - virtual bool onEvent(const SkEvent&); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - -private: - SkPaint fPaint[kAttrCount]; - SkListSource* fSource; - SkScalar fRowHeight; - int fCurrIndex; // logical index - int fScrollIndex; // logical index of top-most visible row - int fVisibleRowCount; - SkString* fStrCache; - - void dirtyStrCache(); - void ensureStrCache(int visibleCount); - - int logicalToVisualIndex(int index) const { return index - fScrollIndex; } - void invalSelection(); - bool getRowRect(int index, SkRect*) const; - void ensureSelectionIsVisible(); - - typedef SkView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////// - -class SkGridView : public SkView { -public: - SkGridView(uint32_t flags = 0); - virtual ~SkGridView(); - - void getCellSize(SkPoint*) const; - void setCellSize(SkScalar x, SkScalar y); - - /** Return the index of the selected item, or -1 if none - */ - int getSelection() const { return fCurrIndex; } - /** Set the index of the selected row, or -1 for none - */ - void setSelection(int); - - void moveSelectionUp(); - void moveSelectionDown(); - - enum Attr { - kBG_Attr, - kHiliteCell_Attr, - kAttrCount - }; - SkPaint& paint(Attr); - - SkListSource* getListSource() const { return fSource; } - SkListSource* setListSource(SkListSource*); - -protected: - virtual void onDraw(SkCanvas*); - virtual void onSizeChange(); - virtual bool onEvent(const SkEvent&); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - -private: - SkView* fScrollBar; - SkPaint fPaint[kAttrCount]; - SkListSource* fSource; - int fCurrIndex; // logical index - - SkPoint fCellSize; - SkIPoint fVisibleCount; - - int logicalToVisualIndex(int index) const { return index; } - void invalSelection(); - bool getCellRect(int index, SkRect*) const; - void ensureSelectionIsVisible(); - - typedef SkView INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/include/views/SkWindow.h b/gfx/skia/skia/include/views/SkWindow.h index f56c27c022..b4fabd253e 100644 --- a/gfx/skia/skia/include/views/SkWindow.h +++ b/gfx/skia/skia/include/views/SkWindow.h @@ -8,6 +8,7 @@ #ifndef SkWindow_DEFINED #define SkWindow_DEFINED +#include "../private/SkTDArray.h" #include "SkView.h" #include "SkBitmap.h" #include "SkMatrix.h" @@ -15,7 +16,6 @@ #include "SkEvent.h" #include "SkKey.h" #include "SkSurfaceProps.h" -#include "SkTDArray.h" class SkSurface; class SkOSMenu; @@ -41,10 +41,12 @@ public: fSurfaceProps = props; } + SkImageInfo info() const { return fBitmap.info(); } const SkBitmap& getBitmap() const { return fBitmap; } - void setColorType(SkColorType); - void resize(int width, int height, SkColorType = kUnknown_SkColorType); + void resize(int width, int height); + void resize(const SkImageInfo&); + void setColorType(SkColorType, SkColorProfileType); bool isDirty() const { return !fDirtyRgn.isEmpty(); } bool update(SkIRect* updateArea); @@ -72,9 +74,6 @@ public: virtual SkSurface* createSurface(); - virtual void onPDFSaved(const char title[], const char desc[], - const char path[]) {} - protected: virtual bool onEvent(const SkEvent&); virtual bool onDispatchClick(int x, int y, Click::State, void* owner, unsigned modi); @@ -99,7 +98,6 @@ protected: private: SkSurfaceProps fSurfaceProps; - SkColorType fColorType; SkBitmap fBitmap; SkRegion fDirtyRgn; diff --git a/gfx/skia/skia/include/views/animated/SkBorderView.h b/gfx/skia/skia/include/views/animated/SkBorderView.h deleted file mode 100644 index 8b1e537a0b..0000000000 --- a/gfx/skia/skia/include/views/animated/SkBorderView.h +++ /dev/null @@ -1,40 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkBorderView_DEFINED -#define SkBorderView_DEFINED - -#include "SkView.h" -#include "SkWidgetViews.h" -#include "SkAnimator.h" - -class SkBorderView : public SkWidgetView { -public: - SkBorderView(); - ~SkBorderView(); - void setSkin(const char skin[]); - SkScalar getLeft() const { return fLeft; } - SkScalar getRight() const { return fRight; } - SkScalar getTop() const { return fTop; } - SkScalar getBottom() const { return fBottom; } -protected: - //overrides - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - virtual void onSizeChange(); - virtual void onDraw(SkCanvas* canvas); - virtual bool onEvent(const SkEvent& evt); -private: - SkAnimator fAnim; - SkScalar fLeft, fRight, fTop, fBottom; //margin on each side - SkRect fMargin; - - typedef SkWidgetView INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/include/views/animated/SkImageView.h b/gfx/skia/skia/include/views/animated/SkImageView.h deleted file mode 100644 index a21da0be51..0000000000 --- a/gfx/skia/skia/include/views/animated/SkImageView.h +++ /dev/null @@ -1,68 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkImageView_DEFINED -#define SkImageView_DEFINED - -#include "SkView.h" -#include "SkString.h" - -class SkAnimator; -class SkBitmap; -class SkMatrix; - -class SkImageView : public SkView { -public: - SkImageView(); - virtual ~SkImageView(); - - void getUri(SkString*) const; - void setUri(const char []); - void setUri(const SkString&); - - - enum ScaleType { - kMatrix_ScaleType, - kFitXY_ScaleType, - kFitStart_ScaleType, - kFitCenter_ScaleType, - kFitEnd_ScaleType - }; - ScaleType getScaleType() const { return (ScaleType)fScaleType; } - void setScaleType(ScaleType); - - bool getImageMatrix(SkMatrix*) const; - void setImageMatrix(const SkMatrix*); - -protected: - // overrides - virtual bool onEvent(const SkEvent&); - virtual void onDraw(SkCanvas*); - virtual void onInflate(const SkDOM&, const SkDOMNode*); - -private: - SkString fUri; - SkMatrix* fMatrix; // null or copy of caller's matrix ,,,,, - union { - SkAnimator* fAnim; - SkBitmap* fBitmap; - } fData; - uint8_t fScaleType; - SkBool8 fDataIsAnim; // as opposed to bitmap - SkBool8 fUriIsValid; - - void onUriChange(); - bool getDataBounds(SkRect* bounds); - bool freeData(); - bool ensureUriIsLoaded(); - - typedef SkView INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/include/views/animated/SkProgressBarView.h b/gfx/skia/skia/include/views/animated/SkProgressBarView.h deleted file mode 100644 index 7e670a9ad5..0000000000 --- a/gfx/skia/skia/include/views/animated/SkProgressBarView.h +++ /dev/null @@ -1,50 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkProgressBarView_DEFINED -#define SkProgressBarView_DEFINED - -#include "SkView.h" -#include "SkWidgetViews.h" -#include "SkAnimator.h" - -class SkProgressBarView : public SkWidgetView { - public: - SkProgressBarView(); - //SkProgressBarView(int max); - - //inflate: "sk-progress" - - void reset(); //reset progress to zero - void setProgress(int progress); - void changeProgress(int diff); - void setMax(int max); - - int getProgress() const { return fProgress; } - int getMax() const { return fMax; } - - protected: - //overrides - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - virtual void onSizeChange(); - virtual void onDraw(SkCanvas* canvas); - virtual bool onEvent(const SkEvent& evt); - - private: - SkAnimator fAnim; - int fProgress; - int fMax; - - typedef SkWidgetView INHERITED; -}; - - - - -#endif diff --git a/gfx/skia/skia/include/views/animated/SkScrollBarView.h b/gfx/skia/skia/include/views/animated/SkScrollBarView.h deleted file mode 100644 index 05042f06fe..0000000000 --- a/gfx/skia/skia/include/views/animated/SkScrollBarView.h +++ /dev/null @@ -1,44 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkScrollBarView_DEFINED -#define SkScrollBarView_DEFINED - -#include "SkView.h" -#include "SkWidgetViews.h" -#include "SkAnimator.h" - -class SkScrollBarView : public SkWidgetView { -public: - SkScrollBarView(); - - unsigned getStart() const { return fStartPoint; } - unsigned getShown() const { return fShownLength; } - unsigned getTotal() const { return fTotalLength; } - - void setStart(unsigned start); - void setShown(unsigned shown); - void setTotal(unsigned total); - -protected: - //overrides - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - virtual void onSizeChange(); - virtual void onDraw(SkCanvas* canvas); - virtual bool onEvent(const SkEvent& evt); - -private: - SkAnimator fAnim; - unsigned fTotalLength, fStartPoint, fShownLength; - - void adjust(); - - typedef SkWidgetView INHERITED; -}; -#endif diff --git a/gfx/skia/skia/include/views/animated/SkWidgetViews.h b/gfx/skia/skia/include/views/animated/SkWidgetViews.h deleted file mode 100644 index 728eec2876..0000000000 --- a/gfx/skia/skia/include/views/animated/SkWidgetViews.h +++ /dev/null @@ -1,309 +0,0 @@ - -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - - -#ifndef SkWidgetViews_DEFINED -#define SkWidgetViews_DEFINED - -#include "SkView.h" - - -enum SkWidgetEnum { - kBorder_WidgetEnum, //!< - kButton_WidgetEnum, //!< - kImage_WidgetEnum, //!< - kList_WidgetEnum, //!< - kProgress_WidgetEnum, //!< - kScroll_WidgetEnum, //!< - kText_WidgetEnum, //!< - - kWidgetEnumCount -}; - -//determines which skin to use -enum SkinEnum { - kBorder_SkinEnum, - kButton_SkinEnum, - kProgress_SkinEnum, - kScroll_SkinEnum, - kStaticText_SkinEnum, - - kSkinEnumCount -}; - -#include "SkAnimator.h" -//used for inflates -const char* get_skin_enum_path(SkinEnum se); -void init_skin_anim(const char path[], SkAnimator* anim); -void init_skin_anim(SkinEnum se, SkAnimator* anim); -void init_skin_paint(SkinEnum se, SkPaint* paint); -void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint); - -/** Given an enum value, return an instance of the specified widget. - If the enum is out of range, returns null -*/ -SkView* SkWidgetFactory(SkWidgetEnum); -/** Given the inflate/element name of a widget, return an instance of - the specified widget, or null if name does not match any known - widget type. -*/ -SkView* SkWidgetFactory(const char name[]); - -//////////////////////////////////////////////////////////////////////////////////////////////// - -class SkWidgetView : public SkView { -public: - SkWidgetView(); - - const char* getLabel() const; - void getLabel(SkString* label) const; - - void setLabel(const char[]); - void setLabel(const char[], size_t len); - void setLabel(const SkString&); - - SkEvent& event() { return fEvent; } - const SkEvent& event() const { return fEvent; } - - /** Returns true if the widget can post its event to its listeners. - */ - bool postWidgetEvent(); - - /** Returns the sinkID of the widgetview that posted the event, or 0 - */ - static SkEventSinkID GetWidgetEventSinkID(const SkEvent&); - -protected: - /** called when the label changes. override in subclasses. default action invals the view's bounds. - called with the old and new labels, before the label has actually changed. - */ - virtual void onLabelChange(const char oldLabel[], const char newLabel[]); - /** called before posting the event to our listeners. Override to add slots to the event - before posting. Return true to proceed with posting, or false to not post the event to any - listener. Note: the event passed in may not be the same as calling this->event(). - Be sure to call your INHERITED method as well, so that all classes in the hierarchy get a shot - at modifying the event (and possibly returning false to abort). - */ - virtual bool onPrepareWidgetEvent(SkEvent* evt); - - // overrides - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); - -private: - SkString fLabel; - SkEvent fEvent; - - typedef SkView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////// - -class SkButtonView : public SkWidgetView { -public: - // inflate: "sk-button" - -protected: - // overrides - virtual bool onEvent(const SkEvent&); -private: - typedef SkWidgetView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////// - -class SkCheckButtonView : public SkWidgetView { -public: - SkCheckButtonView(); - - // inflate: "sk-checkbutton" - - enum CheckState { - kOff_CheckState, //!< inflate: check-state="off" - kOn_CheckState, //!< inflate: check-state="on" - kUnknown_CheckState //!< inflate: check-state="unknown" - }; - CheckState getCheckState() const { return (CheckState)fCheckState; } - void setCheckState(CheckState); - - /** use this to extract the CheckState from an event (i.e. one that as posted - by a SkCheckButtonView). Returns true if the proper slot was present in the event, - and sets state to that value. If no proper slot is found, returns false and does not - modify state. - */ - static bool GetWidgetEventCheckState(const SkEvent&, CheckState* state); - -protected: - // called when the check-state is about to change, but before it actually has - virtual void onCheckStateChange(CheckState oldState, CheckState newState); - - // overrides - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); - virtual bool onPrepareWidgetEvent(SkEvent* evt); - -private: - uint8_t fCheckState; - - typedef SkWidgetView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////// -#include "SkTextBox.h" - -class SkStaticTextView : public SkView { -public: - SkStaticTextView(); - virtual ~SkStaticTextView(); - - enum Mode { - kFixedSize_Mode, - kAutoWidth_Mode, - kAutoHeight_Mode, - - kModeCount - }; - Mode getMode() const { return (Mode)fMode; } - void setMode(Mode); - - SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; } - void setSpacingAlign(SkTextBox::SpacingAlign); - - void getMargin(SkPoint* margin) const; - void setMargin(SkScalar dx, SkScalar dy); - - size_t getText(SkString* text = NULL) const; - size_t getText(char text[] = NULL) const; - void setText(const SkString&); - void setText(const char text[]); - void setText(const char text[], size_t len); - - void getPaint(SkPaint*) const; - void setPaint(const SkPaint&); - -protected: - // overrides - virtual void onDraw(SkCanvas*); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node*); - -private: - SkPoint fMargin; - SkString fText; - SkPaint fPaint; - uint8_t fMode; - uint8_t fSpacingAlign; - - void computeSize(); - - typedef SkView INHERITED; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////// - -class SkAnimator; -class SkListSource; -class SkScrollBarView; - -class SkListView : public SkWidgetView { -public: - SkListView(); - virtual ~SkListView(); - - bool hasScrollBar() const { return fScrollBar != NULL; } - void setHasScrollBar(bool); - - /** Return the number of visible rows - */ - int getVisibleRowCount() const { return fVisibleRowCount; } - /** Return the index of the selected row, or -1 if none - */ - int getSelection() const { return fCurrIndex; } - /** Set the index of the selected row, or -1 for none - */ - void setSelection(int); - /** If possible, move the selection up and return true, - else do nothing and return false - If nothing is selected, select the last item (unless there are no items). - */ - bool moveSelectionUp(); - /** If possible, move the selection down and return true, - else do nothing and return false. - If nothing is selected, select the first item (unless there are no items). - */ - bool moveSelectionDown(); - - SkListSource* getListSource() const { return fSource; } - SkListSource* setListSource(SkListSource*); - - /** Call this in your event handler. If the specified event is from a SkListView, - then it returns the index of the selected item in this list, otherwise it - returns -1 - */ - static int GetWidgetEventListIndex(const SkEvent&); - -protected: - // overrides - virtual void onDraw(SkCanvas*); - virtual void onSizeChange(); - virtual bool onEvent(const SkEvent&); - virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node); - virtual bool onPrepareWidgetEvent(SkEvent*); - -private: - enum DirtyFlags { - kAnimCount_DirtyFlag = 0x01, - kAnimContent_DirtyFlag = 0x02 - }; - void dirtyCache(unsigned dirtyFlags); - bool ensureCache(); - - int logicalToVisualIndex(int index) const { return index - fScrollIndex; } - void invalSelection(); - SkScalar getContentWidth() const; - bool getRowRect(int index, SkRect*) const; - void ensureSelectionIsVisible(); - void ensureVisibleRowCount(); - - struct BindingRec; - - enum Heights { - kNormal_Height, - kSelected_Height - }; - SkListSource* fSource; - SkScrollBarView* fScrollBar; - SkAnimator* fAnims; - BindingRec* fBindings; - SkString fSkinName; - SkScalar fHeights[2]; - int16_t fScrollIndex, fCurrIndex; - uint16_t fVisibleRowCount, fBindingCount; - SkBool8 fAnimContentDirty; - SkBool8 fAnimFocusDirty; - - typedef SkWidgetView INHERITED; -}; - -class SkListSource : public SkRefCnt { -public: - - - virtual int countFields(); - virtual void getFieldName(int index, SkString* field); - /** Return the index of the named field, or -1 if not found */ - virtual int findFieldIndex(const char field[]); - - virtual int countRecords(); - virtual void getRecord(int rowIndex, int fieldIndex, SkString* data); - - virtual bool prepareWidgetEvent(SkEvent*, int rowIndex); - - static SkListSource* Factory(const char name[]); -private: - typedef SkRefCnt INHERITED; -}; - -#endif diff --git a/gfx/skia/skia/include/xml/SkXMLWriter.h b/gfx/skia/skia/include/xml/SkXMLWriter.h index 4c0935fc21..32901267de 100644 --- a/gfx/skia/skia/include/xml/SkXMLWriter.h +++ b/gfx/skia/skia/include/xml/SkXMLWriter.h @@ -8,7 +8,7 @@ #ifndef SkXMLWriter_DEFINED #define SkXMLWriter_DEFINED -#include "SkTDArray.h" +#include "../private/SkTDArray.h" #include "SkString.h" #include "SkDOM.h" diff --git a/gfx/skia/skia/src/android/SkBitmapRegionDecoder.cpp b/gfx/skia/skia/src/android/SkBitmapRegionDecoder.cpp index a153282b09..101efbda45 100644 --- a/gfx/skia/skia/src/android/SkBitmapRegionDecoder.cpp +++ b/gfx/skia/skia/src/android/SkBitmapRegionDecoder.cpp @@ -11,7 +11,6 @@ #include "SkAndroidCodec.h" #include "SkCodec.h" #include "SkCodecPriv.h" -#include "SkImageDecoder.h" SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( SkData* data, Strategy strategy) { @@ -24,7 +23,7 @@ SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( SkAutoTDelete streamDeleter(stream); switch (strategy) { case kCanvas_Strategy: { - SkAutoTDelete codec(SkCodec::NewFromStream(streamDeleter.detach())); + SkAutoTDelete codec(SkCodec::NewFromStream(streamDeleter.release())); if (nullptr == codec) { SkCodecPrintf("Error: Failed to create decoder.\n"); return nullptr; @@ -47,11 +46,11 @@ SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( SkASSERT(SkCodec::kTopDown_SkScanlineOrder == codec->getScanlineOrder() || SkCodec::kNone_SkScanlineOrder == codec->getScanlineOrder()); - return new SkBitmapRegionCanvas(codec.detach()); + return new SkBitmapRegionCanvas(codec.release()); } case kAndroidCodec_Strategy: { SkAutoTDelete codec = - SkAndroidCodec::NewFromStream(streamDeleter.detach()); + SkAndroidCodec::NewFromStream(streamDeleter.release()); if (nullptr == codec) { SkCodecPrintf("Error: Failed to create codec.\n"); return NULL; @@ -67,7 +66,7 @@ SkBitmapRegionDecoder* SkBitmapRegionDecoder::Create( return nullptr; } - return new SkBitmapRegionCodec(codec.detach()); + return new SkBitmapRegionCodec(codec.release()); } default: SkASSERT(false); diff --git a/gfx/skia/skia/src/animator/SkAnimateMaker.cpp b/gfx/skia/skia/src/animator/SkAnimateMaker.cpp index 0c28c7bd57..5186da04d8 100644 --- a/gfx/skia/skia/src/animator/SkAnimateMaker.cpp +++ b/gfx/skia/skia/src/animator/SkAnimateMaker.cpp @@ -23,7 +23,7 @@ class DefaultTimeline : public SkAnimator::Timeline { virtual SkMSec getMSecs() const { - return SkTime::GetMSecs(); + return SkEvent::GetMSecsSinceStartup(); } } gDefaultTimeline; diff --git a/gfx/skia/skia/src/animator/SkDisplayType.cpp b/gfx/skia/skia/src/animator/SkDisplayType.cpp index 7ee8dab5a4..92d120eb96 100644 --- a/gfx/skia/skia/src/animator/SkDisplayType.cpp +++ b/gfx/skia/skia/src/animator/SkDisplayType.cpp @@ -730,7 +730,7 @@ void SkDisplayType::UnitTest() { SkDisplayable* test = CreateInstance(maker, gTypeNames[index].fType); if (test == nullptr) continue; -#if defined _WIN32 && _MSC_VER >= 1300 && defined _INC_CRTDBG // only on windows, only if using "crtdbg.h" +#if defined _WIN32 && defined _INC_CRTDBG // only on windows, only if using "crtdbg.h" // we know that crtdbg puts 0xfdfdfdfd at the end of the block // look for unitialized memory, signature 0xcdcdcdcd prior to that int* start = (int*) test; diff --git a/gfx/skia/skia/src/animator/SkDrawBitmap.cpp b/gfx/skia/skia/src/animator/SkDrawBitmap.cpp index ce7464d702..64c2850a3e 100644 --- a/gfx/skia/skia/src/animator/SkDrawBitmap.cpp +++ b/gfx/skia/skia/src/animator/SkDrawBitmap.cpp @@ -10,7 +10,8 @@ #include "SkDrawBitmap.h" #include "SkAnimateMaker.h" #include "SkCanvas.h" -#include "SkImageDecoder.h" +#include "SkData.h" +#include "SkImage.h" #include "SkPaint.h" #include "SkStream.h" @@ -181,7 +182,9 @@ void SkImageBaseBitmap::resolve() { fDirty = false; if (base64.fData) { fBitmap.reset(); - SkImageDecoder::DecodeMemory(base64.fData, base64.fLength, &fBitmap); + sk_sp data = SkData::MakeWithoutCopy(base64.fData, base64.fLength); + sk_sp image = SkImage::MakeFromEncoded(data); + image->asLegacyBitmap(&fBitmap, SkImage::kRO_LegacyBitmapMode); } else if (src.size()) { if (fLast.equals(src)) return; @@ -189,9 +192,10 @@ void SkImageBaseBitmap::resolve() { fBitmap.reset(); //SkStream* stream = SkStream::GetURIStream(fUriBase, src.c_str()); - SkAutoTDelete stream(SkStream::NewFromFile(src.c_str())); - if (stream.get()) { - SkImageDecoder::DecodeStream(stream, &fBitmap); + sk_sp data = SkData::MakeFromFileName(src.c_str()); + if (data) { + sk_sp image = SkImage::MakeFromEncoded(data); + image->asLegacyBitmap(&fBitmap, SkImage::kRO_LegacyBitmapMode); } } } diff --git a/gfx/skia/skia/src/animator/SkDrawBitmap.h b/gfx/skia/skia/src/animator/SkDrawBitmap.h index 270545d0ee..9fd25d604a 100644 --- a/gfx/skia/skia/src/animator/SkDrawBitmap.h +++ b/gfx/skia/skia/src/animator/SkDrawBitmap.h @@ -13,7 +13,6 @@ #include "SkBoundable.h" #include "SkBase64.h" #include "SkBitmap.h" -// #include "SkImageDecoder.h" #include "SkMemberInfo.h" class SkBaseBitmap : public SkBoundable { diff --git a/gfx/skia/skia/src/animator/SkDrawBlur.cpp b/gfx/skia/skia/src/animator/SkDrawBlur.cpp index 14f34acf9b..0acb13ec5a 100644 --- a/gfx/skia/skia/src/animator/SkDrawBlur.cpp +++ b/gfx/skia/skia/src/animator/SkDrawBlur.cpp @@ -29,5 +29,5 @@ SkMaskFilter* SkDrawBlur::getMaskFilter() { if (fSigma <= 0) { return nullptr; } - return SkBlurMaskFilter::Create((SkBlurStyle)fBlurStyle, fSigma); + return SkBlurMaskFilter::Make((SkBlurStyle)fBlurStyle, fSigma).release(); } diff --git a/gfx/skia/skia/src/animator/SkDrawColor.cpp b/gfx/skia/skia/src/animator/SkDrawColor.cpp index c2d0dae8fe..529d9a5954 100644 --- a/gfx/skia/skia/src/animator/SkDrawColor.cpp +++ b/gfx/skia/skia/src/animator/SkDrawColor.cpp @@ -52,7 +52,7 @@ static SkScalar RGB_to_HSV(SkColor color, HSV_Choice choice) { return hue; } -#if defined _WIN32 && _MSC_VER >= 1300 // disable 'red', etc. may be used without having been initialized +#if defined _WIN32 // disable 'red', etc. may be used without having been initialized #pragma warning ( push ) #pragma warning ( disable : 4701 ) #endif @@ -89,7 +89,7 @@ static SkColor HSV_to_RGB(SkColor color, HSV_Choice choice, SkScalar hsv) { SkScalarRoundToInt(green), SkScalarRoundToInt(blue)); } -#if defined _WIN32 && _MSC_VER >= 1300 +#if defined _WIN32 #pragma warning ( pop ) #endif diff --git a/gfx/skia/skia/src/animator/SkDrawDash.cpp b/gfx/skia/skia/src/animator/SkDrawDash.cpp index d47db416fb..e7fcc84107 100644 --- a/gfx/skia/skia/src/animator/SkDrawDash.cpp +++ b/gfx/skia/skia/src/animator/SkDrawDash.cpp @@ -31,5 +31,5 @@ SkPathEffect* SkDash::getPathEffect() { int count = intervals.count(); if (count == 0) return nullptr; - return SkDashPathEffect::Create(intervals.begin(), count, phase); + return SkDashPathEffect::Make(intervals.begin(), count, phase).release(); } diff --git a/gfx/skia/skia/src/animator/SkDrawDiscrete.cpp b/gfx/skia/skia/src/animator/SkDrawDiscrete.cpp index 14917839b2..11963a1d2f 100644 --- a/gfx/skia/skia/src/animator/SkDrawDiscrete.cpp +++ b/gfx/skia/skia/src/animator/SkDrawDiscrete.cpp @@ -30,5 +30,5 @@ SkPathEffect* SkDiscrete::getPathEffect() { if (deviation <= 0 || segLength <= 0) return nullptr; else - return SkDiscretePathEffect::Create(segLength, deviation); + return SkDiscretePathEffect::Make(segLength, deviation).release(); } diff --git a/gfx/skia/skia/src/animator/SkDrawEmboss.cpp b/gfx/skia/skia/src/animator/SkDrawEmboss.cpp index a1cd4dcd16..9f50f2449e 100644 --- a/gfx/skia/skia/src/animator/SkDrawEmboss.cpp +++ b/gfx/skia/skia/src/animator/SkDrawEmboss.cpp @@ -29,6 +29,6 @@ SkDrawEmboss::SkDrawEmboss() : fSigma(-1) { SkMaskFilter* SkDrawEmboss::getMaskFilter() { if (fSigma < 0 || fDirection.count() !=3) return nullptr; - return SkBlurMaskFilter::CreateEmboss(fSigma, fDirection.begin(), - fAmbient, fSpecular); + return SkBlurMaskFilter::MakeEmboss(fSigma, fDirection.begin(), + fAmbient, fSpecular).release(); } diff --git a/gfx/skia/skia/src/animator/SkDrawExtraPathEffect.cpp b/gfx/skia/skia/src/animator/SkDrawExtraPathEffect.cpp index 4fa76c65c9..a444bc1cb3 100644 --- a/gfx/skia/skia/src/animator/SkDrawExtraPathEffect.cpp +++ b/gfx/skia/skia/src/animator/SkDrawExtraPathEffect.cpp @@ -379,12 +379,9 @@ bool SkDrawComposePathEffect::addChild(SkAnimateMaker& , SkDisplayable* child) { } SkPathEffect* SkDrawComposePathEffect::getPathEffect() { - SkPathEffect* e1 = effect1->getPathEffect(); - SkPathEffect* e2 = effect2->getPathEffect(); - SkPathEffect* composite = SkComposePathEffect::Create(e1, e2); - e1->unref(); - e2->unref(); - return composite; + auto e1 = sk_sp(effect1->getPathEffect()); + auto e2 = sk_sp(effect2->getPathEffect()); + return SkComposePathEffect::Make(e1, e2).release(); } bool SkDrawComposePathEffect::isPaint() const { @@ -411,7 +408,7 @@ SkDrawCornerPathEffect::~SkDrawCornerPathEffect() { } SkPathEffect* SkDrawCornerPathEffect::getPathEffect() { - return SkCornerPathEffect::Create(radius); + return SkCornerPathEffect::Make(radius).release(); } ///////// diff --git a/gfx/skia/skia/include/effects/SkDrawExtraPathEffect.h b/gfx/skia/skia/src/animator/SkDrawExtraPathEffect.h similarity index 100% rename from gfx/skia/skia/include/effects/SkDrawExtraPathEffect.h rename to gfx/skia/skia/src/animator/SkDrawExtraPathEffect.h diff --git a/gfx/skia/skia/src/animator/SkDrawGradient.cpp b/gfx/skia/skia/src/animator/SkDrawGradient.cpp index 4aecbc3936..2fa4502f6b 100644 --- a/gfx/skia/skia/src/animator/SkDrawGradient.cpp +++ b/gfx/skia/skia/src/animator/SkDrawGradient.cpp @@ -126,14 +126,12 @@ void SkDrawLinearGradient::dump(SkAnimateMaker* maker) { #endif SkShader* SkDrawLinearGradient::getShader() { - if (addPrelude() == 0 || points.count() != 4) + if (addPrelude() == 0 || points.count() != 4) { return nullptr; - SkShader* shader = SkGradientShader::CreateLinear((SkPoint*)points.begin(), + } + return SkGradientShader::MakeLinear((SkPoint*)points.begin(), fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, - 0, getMatrix()); - SkAutoTDelete autoDel(shader); - (void)autoDel.detach(); - return shader; + 0, getMatrix()).release(); } @@ -161,12 +159,10 @@ void SkDrawRadialGradient::dump(SkAnimateMaker* maker) { #endif SkShader* SkDrawRadialGradient::getShader() { - if (addPrelude() == 0) + if (addPrelude() == 0) { return nullptr; - SkShader* shader = SkGradientShader::CreateRadial(center, + } + return SkGradientShader::MakeRadial(center, radius, fColors.begin(), offsets.begin(), fColors.count(), (SkShader::TileMode) tileMode, - 0, getMatrix()); - SkAutoTDelete autoDel(shader); - (void)autoDel.detach(); - return shader; + 0, getMatrix()).release(); } diff --git a/gfx/skia/skia/src/animator/SkDrawPaint.cpp b/gfx/skia/skia/src/animator/SkDrawPaint.cpp index d1af4f42ff..1336ea2dc4 100644 --- a/gfx/skia/skia/src/animator/SkDrawPaint.cpp +++ b/gfx/skia/skia/src/animator/SkDrawPaint.cpp @@ -229,15 +229,15 @@ void SkDrawPaint::setupPaint(SkPaint* paint) const { if (maskFilter == nullptr) paint->setMaskFilter(nullptr); else if (maskFilter != (SkDrawMaskFilter*) -1) - SkSafeUnref(paint->setMaskFilter(maskFilter->getMaskFilter())); + paint->setMaskFilter(sk_sp(maskFilter->getMaskFilter())); if (pathEffect == nullptr) paint->setPathEffect(nullptr); else if (pathEffect != (SkDrawPathEffect*) -1) - SkSafeUnref(paint->setPathEffect(pathEffect->getPathEffect())); + paint->setPathEffect(sk_ref_sp(pathEffect->getPathEffect())); if (shader == nullptr) paint->setShader(nullptr); else if (shader != (SkDrawShader*) -1) - SkSafeUnref(paint->setShader(shader->getShader())); + paint->setShader(sk_ref_sp(shader->getShader())); if (strikeThru != -1) paint->setStrikeThruText(SkToBool(strikeThru)); if (strokeCap != -1) diff --git a/gfx/skia/skia/src/animator/SkDrawShader.cpp b/gfx/skia/skia/src/animator/SkDrawShader.cpp index 9e3becc48b..2868b9f4ef 100644 --- a/gfx/skia/skia/src/animator/SkDrawShader.cpp +++ b/gfx/skia/skia/src/animator/SkDrawShader.cpp @@ -71,8 +71,8 @@ SkShader* SkDrawBitmapShader::getShader() { // // oops, bitmapshader no longer takes filterBitmap, but deduces it at // draw-time from the paint - return SkShader::CreateBitmapShader(image->fBitmap, + return SkShader::MakeBitmapShader(image->fBitmap, (SkShader::TileMode) tileMode, (SkShader::TileMode) tileMode, - getMatrix()); + getMatrix()).release(); } diff --git a/gfx/skia/skia/src/animator/SkSnapshot.h b/gfx/skia/skia/src/animator/SkSnapshot.h index a4eb175777..003a9dc796 100644 --- a/gfx/skia/skia/src/animator/SkSnapshot.h +++ b/gfx/skia/skia/src/animator/SkSnapshot.h @@ -11,7 +11,6 @@ #define SkSnapShot_DEFINED #include "SkADrawable.h" -#include "SkImageDecoder.h" #include "SkMemberInfo.h" #include "SkString.h" diff --git a/gfx/skia/skia/include/core/SkTDStack.h b/gfx/skia/skia/src/animator/SkTDStack.h similarity index 100% rename from gfx/skia/skia/include/core/SkTDStack.h rename to gfx/skia/skia/src/animator/SkTDStack.h diff --git a/gfx/skia/skia/src/animator/SkTime.cpp b/gfx/skia/skia/src/animator/SkTime.cpp deleted file mode 100644 index 4ee60bf39d..0000000000 --- a/gfx/skia/skia/src/animator/SkTime.cpp +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Copyright 2006 The Android Open Source Project - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ diff --git a/gfx/skia/skia/src/c/sk_paint.cpp b/gfx/skia/skia/src/c/sk_paint.cpp index dd0733f02e..f82cd815c1 100644 --- a/gfx/skia/skia/src/c/sk_paint.cpp +++ b/gfx/skia/skia/src/c/sk_paint.cpp @@ -5,7 +5,9 @@ * found in the LICENSE file. */ +#include "SkMaskFilter.h" #include "SkPaint.h" +#include "SkShader.h" #include "sk_paint.h" #include "sk_types_priv.h" @@ -63,11 +65,11 @@ void sk_paint_set_color(sk_paint_t* cpaint, sk_color_t c) { } void sk_paint_set_shader(sk_paint_t* cpaint, sk_shader_t* cshader) { - AsPaint(cpaint)->setShader(AsShader(cshader)); + AsPaint(cpaint)->setShader(sk_ref_sp(AsShader(cshader))); } void sk_paint_set_maskfilter(sk_paint_t* cpaint, sk_maskfilter_t* cfilter) { - AsPaint(cpaint)->setMaskFilter(AsMaskFilter(cfilter)); + AsPaint(cpaint)->setMaskFilter(sk_ref_sp(AsMaskFilter(cfilter))); } bool sk_paint_is_stroke(const sk_paint_t* cpaint) { diff --git a/gfx/skia/skia/src/c/sk_surface.cpp b/gfx/skia/skia/src/c/sk_surface.cpp index b8fbc63f63..c9b25675a0 100644 --- a/gfx/skia/skia/src/c/sk_surface.cpp +++ b/gfx/skia/skia/src/c/sk_surface.cpp @@ -224,12 +224,12 @@ sk_image_t* sk_image_new_raster_copy(const sk_imageinfo_t* cinfo, const void* pi if (!from_c_info(*cinfo, &info)) { return NULL; } - return (sk_image_t*)SkImage::NewRasterCopy(info, pixels, rowBytes); + return (sk_image_t*)SkImage::MakeRasterCopy(SkPixmap(info, pixels, rowBytes)).release(); } sk_image_t* sk_image_new_from_encoded(const sk_data_t* cdata, const sk_irect_t* subset) { - return ToImage(SkImage::NewFromEncoded(AsData(cdata), - reinterpret_cast(subset))); + return ToImage(SkImage::MakeFromEncoded(sk_ref_sp(AsData(cdata)), + reinterpret_cast(subset)).release()); } sk_data_t* sk_image_encode(const sk_image_t* cimage) { @@ -375,6 +375,11 @@ void sk_canvas_draw_rect(sk_canvas_t* ccanvas, const sk_rect_t* crect, const sk_ AsCanvas(ccanvas)->drawRect(AsRect(*crect), AsPaint(*cpaint)); } +void sk_canvas_draw_circle(sk_canvas_t* ccanvas, float cx, float cy, float rad, + const sk_paint_t* cpaint) { + AsCanvas(ccanvas)->drawCircle(cx, cy, rad, AsPaint(*cpaint)); +} + void sk_canvas_draw_oval(sk_canvas_t* ccanvas, const sk_rect_t* crect, const sk_paint_t* cpaint) { AsCanvas(ccanvas)->drawOval(AsRect(*crect), AsPaint(*cpaint)); } @@ -428,7 +433,7 @@ sk_surface_t* sk_surface_new_raster(const sk_imageinfo_t* cinfo, } SkSurfaceProps surfProps(0, geo); - return (sk_surface_t*)SkSurface::NewRaster(info, &surfProps); + return (sk_surface_t*)SkSurface::MakeRaster(info, &surfProps).release(); } sk_surface_t* sk_surface_new_raster_direct(const sk_imageinfo_t* cinfo, void* pixels, @@ -444,7 +449,7 @@ sk_surface_t* sk_surface_new_raster_direct(const sk_imageinfo_t* cinfo, void* pi } SkSurfaceProps surfProps(0, geo); - return (sk_surface_t*)SkSurface::NewRasterDirect(info, pixels, rowBytes, &surfProps); + return (sk_surface_t*)SkSurface::MakeRasterDirect(info, pixels, rowBytes, &surfProps).release(); } void sk_surface_unref(sk_surface_t* csurf) { @@ -458,7 +463,7 @@ sk_canvas_t* sk_surface_get_canvas(sk_surface_t* csurf) { sk_image_t* sk_surface_new_image_snapshot(sk_surface_t* csurf) { SkSurface* surf = (SkSurface*)csurf; - return (sk_image_t*)surf->newImageSnapshot(); + return (sk_image_t*)surf->makeImageSnapshot().release(); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -477,7 +482,7 @@ sk_canvas_t* sk_picture_recorder_begin_recording(sk_picture_recorder_t* crec, } sk_picture_t* sk_picture_recorder_end_recording(sk_picture_recorder_t* crec) { - return ToPicture(AsPictureRecorder(crec)->endRecording()); + return ToPicture(AsPictureRecorder(crec)->finishRecordingAsPicture().release()); } void sk_picture_ref(sk_picture_t* cpic) { @@ -546,10 +551,10 @@ sk_shader_t* sk_shader_new_linear_gradient(const sk_point_t pts[2], } else { matrix.setIdentity(); } - SkShader* s = SkGradientShader::CreateLinear(reinterpret_cast(pts), - reinterpret_cast(colors), - colorPos, colorCount, mode, 0, &matrix); - return (sk_shader_t*)s; + return (sk_shader_t*)SkGradientShader::MakeLinear(reinterpret_cast(pts), + reinterpret_cast(colors), + colorPos, colorCount, + mode, 0, &matrix).release(); } static const SkPoint& to_skpoint(const sk_point_t& p) { @@ -574,12 +579,10 @@ sk_shader_t* sk_shader_new_radial_gradient(const sk_point_t* ccenter, matrix.setIdentity(); } SkPoint center = to_skpoint(*ccenter); - SkShader* s = SkGradientShader::CreateRadial( - center, (SkScalar)radius, - reinterpret_cast(colors), - reinterpret_cast(colorPos), - colorCount, mode, 0, &matrix); - return (sk_shader_t*)s; + return (sk_shader_t*)SkGradientShader::MakeRadial(center, (SkScalar)radius, + reinterpret_cast(colors), + reinterpret_cast(colorPos), + colorCount, mode, 0, &matrix).release(); } sk_shader_t* sk_shader_new_sweep_gradient(const sk_point_t* ccenter, @@ -593,13 +596,11 @@ sk_shader_t* sk_shader_new_sweep_gradient(const sk_point_t* ccenter, } else { matrix.setIdentity(); } - SkShader* s = SkGradientShader::CreateSweep( - (SkScalar)(ccenter->x), - (SkScalar)(ccenter->y), - reinterpret_cast(colors), - reinterpret_cast(colorPos), - colorCount, 0, &matrix); - return (sk_shader_t*)s; + return (sk_shader_t*)SkGradientShader::MakeSweep((SkScalar)(ccenter->x), + (SkScalar)(ccenter->y), + reinterpret_cast(colors), + reinterpret_cast(colorPos), + colorCount, 0, &matrix).release(); } sk_shader_t* sk_shader_new_two_point_conical_gradient(const sk_point_t* start, @@ -623,13 +624,11 @@ sk_shader_t* sk_shader_new_two_point_conical_gradient(const sk_point_t* start, } SkPoint skstart = to_skpoint(*start); SkPoint skend = to_skpoint(*end); - SkShader* s = SkGradientShader::CreateTwoPointConical( - skstart, (SkScalar)startRadius, - skend, (SkScalar)endRadius, - reinterpret_cast(colors), - reinterpret_cast(colorPos), - colorCount, mode, 0, &matrix); - return (sk_shader_t*)s; + return (sk_shader_t*)SkGradientShader::MakeTwoPointConical(skstart, (SkScalar)startRadius, + skend, (SkScalar)endRadius, + reinterpret_cast(colors), + reinterpret_cast(colorPos), + colorCount, mode, 0, &matrix).release(); } /////////////////////////////////////////////////////////////////////////////////////////// @@ -672,21 +671,21 @@ sk_maskfilter_t* sk_maskfilter_new_blur(sk_blurstyle_t cstyle, float sigma) { if (!find_blurstyle(cstyle, &style)) { return NULL; } - return ToMaskFilter(SkBlurMaskFilter::Create(style, sigma)); + return ToMaskFilter(SkBlurMaskFilter::Make(style, sigma).release()); } /////////////////////////////////////////////////////////////////////////////////////////// sk_data_t* sk_data_new_with_copy(const void* src, size_t length) { - return ToData(SkData::NewWithCopy(src, length)); + return ToData(SkData::MakeWithCopy(src, length).release()); } sk_data_t* sk_data_new_from_malloc(const void* memory, size_t length) { - return ToData(SkData::NewFromMalloc(memory, length)); + return ToData(SkData::MakeFromMalloc(memory, length).release()); } sk_data_t* sk_data_new_subset(const sk_data_t* csrc, size_t offset, size_t length) { - return ToData(SkData::NewSubset(AsData(csrc), offset, length)); + return ToData(SkData::MakeSubset(AsData(csrc), offset, length).release()); } void sk_data_ref(const sk_data_t* cdata) { diff --git a/gfx/skia/skia/src/codec/SkAndroidCodec.cpp b/gfx/skia/skia/src/codec/SkAndroidCodec.cpp index d309d58501..6db0991c0d 100644 --- a/gfx/skia/skia/src/codec/SkAndroidCodec.cpp +++ b/gfx/skia/skia/src/codec/SkAndroidCodec.cpp @@ -8,6 +8,7 @@ #include "SkAndroidCodec.h" #include "SkCodec.h" #include "SkCodecPriv.h" +#include "SkRawAdapterCodec.h" #include "SkSampledCodec.h" #include "SkWebpAdapterCodec.h" @@ -28,15 +29,27 @@ SkAndroidCodec* SkAndroidCodec::NewFromStream(SkStream* stream, SkPngChunkReader } switch (codec->getEncodedFormat()) { - case kWEBP_SkEncodedFormat: - return new SkWebpAdapterCodec((SkWebpCodec*) codec.detach()); +#ifdef SK_CODEC_DECODES_PNG case kPNG_SkEncodedFormat: - case kJPEG_SkEncodedFormat: - case kWBMP_SkEncodedFormat: - case kBMP_SkEncodedFormat: - case kGIF_SkEncodedFormat: case kICO_SkEncodedFormat: - return new SkSampledCodec(codec.detach()); +#endif +#ifdef SK_CODEC_DECODES_JPEG + case kJPEG_SkEncodedFormat: +#endif +#ifdef SK_CODEC_DECODES_GIF + case kGIF_SkEncodedFormat: +#endif + case kBMP_SkEncodedFormat: + case kWBMP_SkEncodedFormat: + return new SkSampledCodec(codec.release()); +#ifdef SK_CODEC_DECODES_WEBP + case kWEBP_SkEncodedFormat: + return new SkWebpAdapterCodec((SkWebpCodec*) codec.release()); +#endif +#ifdef SK_CODEC_DECODES_RAW + case kDNG_SkEncodedFormat: + return new SkRawAdapterCodec((SkRawCodec*)codec.release()); +#endif default: return nullptr; } diff --git a/gfx/skia/skia/src/codec/SkBmpCodec.cpp b/gfx/skia/skia/src/codec/SkBmpCodec.cpp index 3302e4f752..ad6f0ddc4d 100644 --- a/gfx/skia/skia/src/codec/SkBmpCodec.cpp +++ b/gfx/skia/skia/src/codec/SkBmpCodec.cpp @@ -286,6 +286,18 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) { switch (compression) { case kNone_BmpCompressionMethod: inputFormat = kStandard_BmpInputFormat; + + // In addition to more standard pixel compression formats, bmp supports + // the use of bit masks to determine pixel components. The standard + // format for representing 16-bit colors is 555 (XRRRRRGGGGGBBBBB), + // which does not map well to any Skia color formats. For this reason, + // we will always enable mask mode with 16 bits per pixel. + if (16 == bitsPerPixel) { + inputMasks.red = 0x7C00; + inputMasks.green = 0x03E0; + inputMasks.blue = 0x001F; + inputFormat = kBitMask_BmpInputFormat; + } break; case k8BitRLE_BmpCompressionMethod: if (bitsPerPixel != 8) { @@ -331,6 +343,27 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) { inputMasks.red = get_int(iBuffer.get(), 36); inputMasks.green = get_int(iBuffer.get(), 40); inputMasks.blue = get_int(iBuffer.get(), 44); + + if (kInfoV2_BmpHeaderType == headerType || + (kInfoV3_BmpHeaderType == headerType && !inIco)) { + break; + } + + // V3+ bmp files introduce an alpha mask and allow the creator of the image + // to use the alpha channels. However, many of these images leave the + // alpha channel blank and expect to be rendered as opaque. This is the + // case for almost all V3 images, so we ignore the alpha mask. For V4+ + // images in kMask mode, we will use the alpha mask. Additionally, V3 + // bmp-in-ico expect us to use the alpha mask. + // + // skbug.com/4116: We should perhaps also apply the alpha mask in kStandard + // mode. We just haven't seen any images that expect this + // behavior. + // + // Header types are matched based on size. If the header is + // V3+, we are guaranteed to be able to read at least this size. + SkASSERT(infoBytesRemaining > 52); + inputMasks.alpha = get_int(iBuffer.get(), 48); break; case kOS2VX_BmpHeaderType: // TODO: Decide if we intend to support this. @@ -366,90 +399,7 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) { SkCodecPrintf("Error: invalid format for bitmap decoding.\n"); return false; } - - // Most versions of bmps should be rendered as opaque. Either they do - // not have an alpha channel, or they expect the alpha channel to be - // ignored. V3+ bmp files introduce an alpha mask and allow the creator - // of the image to use the alpha channels. However, many of these images - // leave the alpha channel blank and expect to be rendered as opaque. This - // is the case for almost all V3 images, so we render these as opaque. For - // V4+, we will use the alpha channel, and fix the image later if it turns - // out to be fully transparent. - // As an exception, V3 bmp-in-ico may use an alpha mask. - SkAlphaType alphaType = kOpaque_SkAlphaType; - if ((kInfoV3_BmpHeaderType == headerType && inIco) || - kInfoV4_BmpHeaderType == headerType || - kInfoV5_BmpHeaderType == headerType) { - // Header types are matched based on size. If the header is - // V3+, we are guaranteed to be able to read at least this size. - SkASSERT(infoBytesRemaining > 52); - inputMasks.alpha = get_int(iBuffer.get(), 48); - if (inputMasks.alpha != 0) { - alphaType = kUnpremul_SkAlphaType; - } - } - iBuffer.free(); - - // Additionally, 32 bit bmp-in-icos use the alpha channel. - // FIXME (msarett): Don't all bmp-in-icos use the alpha channel? - // And, RLE inputs may skip pixels, leaving them as transparent. This - // is uncommon, but we cannot be certain that an RLE bmp will be opaque. - if ((inIco && 32 == bitsPerPixel) || (kRLE_BmpInputFormat == inputFormat)) { - alphaType = kUnpremul_SkAlphaType; - } - - // Check for valid bits per pixel. - // At the same time, use this information to choose a suggested color type - // and to set default masks. - SkColorType colorType = kN32_SkColorType; - switch (bitsPerPixel) { - // In addition to more standard pixel compression formats, bmp supports - // the use of bit masks to determine pixel components. The standard - // format for representing 16-bit colors is 555 (XRRRRRGGGGGBBBBB), - // which does not map well to any Skia color formats. For this reason, - // we will always enable mask mode with 16 bits per pixel. - case 16: - if (kBitMask_BmpInputFormat != inputFormat) { - inputMasks.red = 0x7C00; - inputMasks.green = 0x03E0; - inputMasks.blue = 0x001F; - inputFormat = kBitMask_BmpInputFormat; - } - break; - // We want to decode to kIndex_8 for input formats that are already - // designed in index format. - case 1: - case 2: - case 4: - case 8: - // However, we cannot in RLE format since we may need to leave some - // pixels as transparent. Similarly, we also cannot for ICO images - // since we may need to apply a transparent mask. - if (kRLE_BmpInputFormat != inputFormat && !inIco) { - colorType = kIndex_8_SkColorType; - } - case 24: - case 32: - break; - default: - SkCodecPrintf("Error: invalid input value for bits per pixel.\n"); - return false; - } - - // Check that input bit masks are valid and create the masks object - SkAutoTDelete - masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel)); - if (nullptr == masks) { - SkCodecPrintf("Error: invalid input masks.\n"); - return false; - } - - // Check for a valid number of total bytes when in RLE mode - if (totalBytes <= offset && kRLE_BmpInputFormat == inputFormat) { - SkCodecPrintf("Error: RLE requires valid input size.\n"); - return false; - } - const size_t RLEBytes = totalBytes - offset; + iBuffer.reset(); // Calculate the number of bytes read so far const uint32_t bytesRead = kBmpHeaderBytes + infoBytes + maskBytes; @@ -461,55 +411,133 @@ bool SkBmpCodec::ReadHeader(SkStream* stream, bool inIco, SkCodec** codecOut) { return false; } - // Skip to the start of the pixel array. - // We can do this here because there is no color table to read - // in bit mask mode. - if (!inIco && kBitMask_BmpInputFormat == inputFormat) { - if (stream->skip(offset - bytesRead) != offset - bytesRead) { - SkCodecPrintf("Error: unable to skip to image data.\n"); - return false; - } - } - if (codecOut) { - // Set the image info - const SkImageInfo& imageInfo = SkImageInfo::Make(width, height, - colorType, alphaType); - // Return the codec - switch (inputFormat) { - case kStandard_BmpInputFormat: + switch (inputFormat) { + case kStandard_BmpInputFormat: { + // BMPs-in-ICOs often contain an alpha mask after the image, which + // means we cannot guarantee that an image is opaque, even if the + // embedded bmp is opaque. + // We use |isOpaque| to indicate if the BMP itself is opaque, but + // still need to recommend kUnpremul when it is contained in an ICO. + SkColorType colorType = kN32_SkColorType; + SkAlphaType alphaType = inIco ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType; + bool isOpaque = true; + switch (bitsPerPixel) { + // Palette formats + case 1: + case 2: + case 4: + case 8: + // We cannot recommend a palette color type for ICOs because they + // may contain a transparency mask. + if (!inIco) { + colorType = kIndex_8_SkColorType; + } + break; + case 24: + case 32: + // 32-bit BMP-in-ICOs actually use the alpha channel in place of a + // transparency mask. + if (inIco) { + isOpaque = false; + } + break; + default: + SkCodecPrintf("Error: invalid input value for bits per pixel.\n"); + return false; + } + + if (codecOut) { // We require streams to have a memory base for Bmp-in-Ico decodes. SkASSERT(!inIco || nullptr != stream->getMemoryBase()); + + // Set the image info and create a codec. + const SkImageInfo imageInfo = SkImageInfo::Make(width, height, colorType, + alphaType); *codecOut = new SkBmpStandardCodec(imageInfo, stream, bitsPerPixel, numColors, - bytesPerColor, offset - bytesRead, rowOrder, inIco); - return true; - case kBitMask_BmpInputFormat: - // Bmp-in-Ico must be standard mode - if (inIco) { - SkCodecPrintf("Error: Icos may not use bit mask format.\n"); + bytesPerColor, offset - bytesRead, rowOrder, isOpaque, inIco); + + } + return true; + } + + case kBitMask_BmpInputFormat: { + // Bmp-in-Ico must be standard mode + if (inIco) { + SkCodecPrintf("Error: Icos may not use bit mask format.\n"); + return false; + } + + switch (bitsPerPixel) { + case 16: + case 24: + case 32: + break; + default: + SkCodecPrintf("Error: invalid input value for bits per pixel.\n"); + return false; + } + + // Skip to the start of the pixel array. + // We can do this here because there is no color table to read + // in bit mask mode. + if (stream->skip(offset - bytesRead) != offset - bytesRead) { + SkCodecPrintf("Error: unable to skip to image data.\n"); + return false; + } + + if (codecOut) { + // Check that input bit masks are valid and create the masks object + SkAutoTDelete masks(SkMasks::CreateMasks(inputMasks, bitsPerPixel)); + if (nullptr == masks) { + SkCodecPrintf("Error: invalid input masks.\n"); return false; } - *codecOut = new SkBmpMaskCodec(imageInfo, stream, bitsPerPixel, masks.detach(), + // Set the image info + SkAlphaType alphaType = masks->getAlphaMask() ? kUnpremul_SkAlphaType : + kOpaque_SkAlphaType; + const SkImageInfo imageInfo = SkImageInfo::Make(width, height, kN32_SkColorType, + alphaType); + *codecOut = new SkBmpMaskCodec(imageInfo, stream, bitsPerPixel, masks.release(), rowOrder); - return true; - case kRLE_BmpInputFormat: - // Bmp-in-Ico must be standard mode - // When inIco is true, this line cannot be reached, since we - // require that RLE Bmps have a valid number of totalBytes, and - // Icos skip the header that contains totalBytes. - SkASSERT(!inIco); + } + return true; + } + + case kRLE_BmpInputFormat: { + // We should not reach this point without a valid value of bitsPerPixel. + SkASSERT(4 == bitsPerPixel || 8 == bitsPerPixel || 24 == bitsPerPixel); + + // Check for a valid number of total bytes when in RLE mode + if (totalBytes <= offset) { + SkCodecPrintf("Error: RLE requires valid input size.\n"); + return false; + } + const size_t RLEBytes = totalBytes - offset; + + // Bmp-in-Ico must be standard mode + // When inIco is true, this line cannot be reached, since we + // require that RLE Bmps have a valid number of totalBytes, and + // Icos skip the header that contains totalBytes. + SkASSERT(!inIco); + + if (codecOut) { + // RLE inputs may skip pixels, leaving them as transparent. This + // is uncommon, but we cannot be certain that an RLE bmp will be + // opaque. + const SkImageInfo imageInfo = SkImageInfo::Make(width, height, kN32_SkColorType, + kUnpremul_SkAlphaType); *codecOut = new SkBmpRLECodec(imageInfo, stream, bitsPerPixel, numColors, bytesPerColor, offset - bytesRead, rowOrder, RLEBytes); - return true; - default: - SkASSERT(false); - return false; + } + return true; } + default: + SkASSERT(false); + return false; } - - return true; } /* @@ -523,7 +551,7 @@ SkCodec* SkBmpCodec::NewFromStream(SkStream* stream, bool inIco) { // codec has taken ownership of stream, so we do not need to // delete it. SkASSERT(codec); - streamDeleter.detach(); + streamDeleter.release(); return codec; } return nullptr; @@ -534,6 +562,7 @@ SkBmpCodec::SkBmpCodec(const SkImageInfo& info, SkStream* stream, : INHERITED(info, stream) , fBitsPerPixel(bitsPerPixel) , fRowOrder(rowOrder) + , fSrcRowBytes(SkAlign4(compute_row_bytes(info.width(), fBitsPerPixel))) {} bool SkBmpCodec::onRewind() { @@ -565,3 +594,12 @@ int SkBmpCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { // Decode the requested rows return this->decodeRows(rowInfo, dst, rowBytes, this->options()); } + +bool SkBmpCodec::skipRows(int count) { + const size_t bytesToSkip = count * fSrcRowBytes; + return this->stream()->skip(bytesToSkip) == bytesToSkip; +} + +bool SkBmpCodec::onSkipScanlines(int count) { + return this->skipRows(count); +} diff --git a/gfx/skia/skia/src/codec/SkBmpCodec.h b/gfx/skia/skia/src/codec/SkBmpCodec.h index 5d77ca3fc8..a7e8e58431 100644 --- a/gfx/skia/skia/src/codec/SkBmpCodec.h +++ b/gfx/skia/skia/src/codec/SkBmpCodec.h @@ -8,6 +8,7 @@ #define SkBmpCodec_DEFINED #include "SkCodec.h" +#include "SkColorSpace.h" #include "SkColorTable.h" #include "SkImageInfo.h" #include "SkStream.h" @@ -81,6 +82,7 @@ protected: */ uint16_t bitsPerPixel() const { return fBitsPerPixel; } SkScanlineOrder onGetScanlineOrder() const override { return fRowOrder; } + size_t srcRowBytes() const { return fSrcRowBytes; } /* * To be overriden by bmp subclasses, which provide unique implementations. @@ -127,15 +129,18 @@ private: virtual int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) = 0; + virtual bool skipRows(int count); + Result onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options&, SkPMColor inputColorPtr[], int* inputColorCount) override; int onGetScanlines(void* dst, int count, size_t rowBytes) override; - // TODO(msarett): Override default skipping with something more clever. + bool onSkipScanlines(int count) override; const uint16_t fBitsPerPixel; const SkScanlineOrder fRowOrder; + const size_t fSrcRowBytes; typedef SkCodec INHERITED; }; diff --git a/gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp b/gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp index b173317c10..21d231b15f 100644 --- a/gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp +++ b/gfx/skia/skia/src/codec/SkBmpMaskCodec.cpp @@ -18,8 +18,7 @@ SkBmpMaskCodec::SkBmpMaskCodec(const SkImageInfo& info, SkStream* stream, : INHERITED(info, stream, bitsPerPixel, rowOrder) , fMasks(masks) , fMaskSwizzler(nullptr) - , fSrcRowBytes(SkAlign4(compute_row_bytes(this->getInfo().width(), this->bitsPerPixel()))) - , fSrcBuffer(new uint8_t [fSrcRowBytes]) + , fSrcBuffer(new uint8_t [this->srcRowBytes()]) {} /* @@ -58,25 +57,12 @@ SkCodec::Result SkBmpMaskCodec::onGetPixels(const SkImageInfo& dstInfo, return kSuccess; } -bool SkBmpMaskCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& options) { - // Create the swizzler - fMaskSwizzler.reset(SkMaskSwizzler::CreateMaskSwizzler(dstInfo, this->getInfo(), fMasks, - this->bitsPerPixel(), options)); - - if (nullptr == fMaskSwizzler.get()) { - return false; - } - - return true; -} - SkCodec::Result SkBmpMaskCodec::prepareToDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options, SkPMColor inputColorPtr[], int* inputColorCount) { - // Initialize a the mask swizzler - if (!this->initializeSwizzler(dstInfo, options)) { - SkCodecPrintf("Error: cannot initialize swizzler.\n"); - return SkCodec::kInvalidConversion; - } + // Initialize the mask swizzler + fMaskSwizzler.reset(SkMaskSwizzler::CreateMaskSwizzler(dstInfo, this->getInfo(), fMasks, + this->bitsPerPixel(), options)); + SkASSERT(fMaskSwizzler); return SkCodec::kSuccess; } @@ -92,7 +78,7 @@ int SkBmpMaskCodec::decodeRows(const SkImageInfo& dstInfo, const int height = dstInfo.height(); for (int y = 0; y < height; y++) { // Read a row of the input - if (this->stream()->read(srcRow, fSrcRowBytes) != fSrcRowBytes) { + if (this->stream()->read(srcRow, this->srcRowBytes()) != this->srcRowBytes()) { SkCodecPrintf("Warning: incomplete input stream.\n"); return y; } diff --git a/gfx/skia/skia/src/codec/SkBmpMaskCodec.h b/gfx/skia/skia/src/codec/SkBmpMaskCodec.h index 4ec868db0e..93b3bca5a0 100644 --- a/gfx/skia/skia/src/codec/SkBmpMaskCodec.h +++ b/gfx/skia/skia/src/codec/SkBmpMaskCodec.h @@ -44,7 +44,6 @@ protected: private: - bool initializeSwizzler(const SkImageInfo& dstInfo, const Options& options); SkSampler* getSampler(bool createIfNecessary) override { SkASSERT(fMaskSwizzler); return fMaskSwizzler; @@ -55,7 +54,6 @@ private: SkAutoTDelete fMasks; // owned SkAutoTDelete fMaskSwizzler; - const size_t fSrcRowBytes; SkAutoTDeleteArray fSrcBuffer; typedef SkBmpCodec INHERITED; diff --git a/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp b/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp index b01012644b..793bbfd260 100644 --- a/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp +++ b/gfx/skia/skia/src/codec/SkBmpRLECodec.cpp @@ -26,6 +26,7 @@ SkBmpRLECodec::SkBmpRLECodec(const SkImageInfo& info, SkStream* stream, , fOffset(offset) , fStreamBuffer(new uint8_t[RLEBytes]) , fRLEBytes(RLEBytes) + , fOrigRLEBytes(RLEBytes) , fCurrRLEByte(0) , fSampleX(1) {} @@ -200,7 +201,7 @@ size_t SkBmpRLECodec::checkForMoreData() { void SkBmpRLECodec::setPixel(void* dst, size_t dstRowBytes, const SkImageInfo& dstInfo, uint32_t x, uint32_t y, uint8_t index) { - if (is_coord_necessary(x, fSampleX, dstInfo.width())) { + if (dst && is_coord_necessary(x, fSampleX, dstInfo.width())) { // Set the row uint32_t row = this->getDstRow(y, dstInfo.height()); @@ -233,7 +234,7 @@ void SkBmpRLECodec::setRGBPixel(void* dst, size_t dstRowBytes, const SkImageInfo& dstInfo, uint32_t x, uint32_t y, uint8_t red, uint8_t green, uint8_t blue) { - if (is_coord_necessary(x, fSampleX, dstInfo.width())) { + if (dst && is_coord_necessary(x, fSampleX, dstInfo.width())) { // Set the row uint32_t row = this->getDstRow(y, dstInfo.height()); @@ -270,6 +271,8 @@ SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo, // Reset fSampleX. If it needs to be a value other than 1, it will get modified by // the sampler. fSampleX = 1; + fLinesToSkip = 0; + // Create the color table if necessary and prepare the stream for decode // Note that if it is non-NULL, inputColorCount will be modified if (!this->createColorTable(inputColorCount)) { @@ -281,9 +284,10 @@ SkCodec::Result SkBmpRLECodec::prepareToDecode(const SkImageInfo& dstInfo, copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount); // Initialize a buffer for encoded RLE data + fRLEBytes = fOrigRLEBytes; if (!this->initializeStreamBuffer()) { SkCodecPrintf("Error: cannot initialize stream buffer.\n"); - return SkCodec::kInvalidConversion; + return SkCodec::kInvalidInput; } return SkCodec::kSuccess; @@ -301,23 +305,35 @@ int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowB static const uint8_t RLE_EOF = 1; static const uint8_t RLE_DELTA = 2; - // Set constant values const int width = this->getInfo().width(); - const int height = info.height(); + int height = info.height(); // Account for sampling. SkImageInfo dstInfo = info.makeWH(get_scaled_dimension(width, fSampleX), height); - // Destination parameters - int x = 0; - int y = 0; - // Set the background as transparent. Then, if the RLE code skips pixels, // the skipped pixels will be transparent. // Because of the need for transparent pixels, kN32 is the only color // type that makes sense for the destination format. SkASSERT(kN32_SkColorType == dstInfo.colorType()); - SkSampler::Fill(dstInfo, dst, dstRowBytes, SK_ColorTRANSPARENT, opts.fZeroInitialized); + if (dst) { + SkSampler::Fill(dstInfo, dst, dstRowBytes, SK_ColorTRANSPARENT, opts.fZeroInitialized); + } + + // Adjust the height and the dst if the previous call to decodeRows() left us + // with lines that need to be skipped. + if (height > fLinesToSkip) { + height -= fLinesToSkip; + dst = SkTAddOffset(dst, fLinesToSkip * dstRowBytes); + fLinesToSkip = 0; + } else { + fLinesToSkip -= height; + return height; + } + + // Destination parameters + int x = 0; + int y = 0; while (true) { // If we have reached a row that is beyond the requested height, we have @@ -366,9 +382,12 @@ int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowB const uint8_t dy = fStreamBuffer.get()[fCurrRLEByte++]; x += dx; y += dy; - if (x > width || y > height) { + if (x > width) { SkCodecPrintf("Warning: invalid RLE input.\n"); return y - dy; + } else if (y > height) { + fLinesToSkip = y - height; + return height; } break; } @@ -483,6 +502,13 @@ int SkBmpRLECodec::decodeRows(const SkImageInfo& info, void* dst, size_t dstRowB } } +bool SkBmpRLECodec::skipRows(int count) { + const SkImageInfo rowInfo = SkImageInfo::Make(this->getInfo().width(), count, kN32_SkColorType, + kUnpremul_SkAlphaType); + + return count == this->decodeRows(rowInfo, nullptr, 0, this->options()); +} + // FIXME: Make SkBmpRLECodec have no knowledge of sampling. // Or it should do all sampling natively. // It currently is a hybrid that needs to know what SkScaledCodec is doing. @@ -503,8 +529,13 @@ private: SkBmpRLECodec* fCodec; }; -SkSampler* SkBmpRLECodec::getSampler(bool createIfNecessary) { - if (!fSampler && createIfNecessary) { +SkSampler* SkBmpRLECodec::getSampler(bool /*createIfNecessary*/) { + // We will always create an SkBmpRLESampler if one is requested. + // This allows clients to always use the SkBmpRLESampler's + // version of fill(), which does nothing since RLE decodes have + // already filled pixel memory. This seems fine, since creating + // an SkBmpRLESampler is pretty inexpensive. + if (!fSampler) { fSampler.reset(new SkBmpRLESampler(this)); } diff --git a/gfx/skia/skia/src/codec/SkBmpRLECodec.h b/gfx/skia/skia/src/codec/SkBmpRLECodec.h index df2a97d845..2ddf8d8b90 100644 --- a/gfx/skia/skia/src/codec/SkBmpRLECodec.h +++ b/gfx/skia/skia/src/codec/SkBmpRLECodec.h @@ -84,9 +84,14 @@ private: const SkImageInfo& dstInfo, uint32_t x, uint32_t y, uint8_t red, uint8_t green, uint8_t blue); + /* + * If dst is NULL, this is a signal to skip the rows. + */ int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) override; + bool skipRows(int count) override; + SkSampler* getSampler(bool createIfNecessary) override; SkAutoTUnref fColorTable; // owned @@ -96,9 +101,16 @@ private: const uint32_t fOffset; SkAutoTDeleteArray fStreamBuffer; size_t fRLEBytes; + const size_t fOrigRLEBytes; uint32_t fCurrRLEByte; int fSampleX; SkAutoTDelete fSampler; + // Scanline decodes allow the client to ask for a single scanline at a time. + // This can be tricky when the RLE encoding instructs the decoder to jump down + // multiple lines. This field keeps track of lines that need to be skipped + // on subsequent calls to decodeRows(). + int fLinesToSkip; + typedef SkBmpCodec INHERITED; }; diff --git a/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp b/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp index 85b40778b6..80a989ae4d 100644 --- a/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp +++ b/gfx/skia/skia/src/codec/SkBmpStandardCodec.cpp @@ -17,15 +17,16 @@ SkBmpStandardCodec::SkBmpStandardCodec(const SkImageInfo& info, SkStream* stream, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, uint32_t offset, - SkCodec::SkScanlineOrder rowOrder, bool inIco) + SkCodec::SkScanlineOrder rowOrder, + bool isOpaque, bool inIco) : INHERITED(info, stream, bitsPerPixel, rowOrder) , fColorTable(nullptr) , fNumColors(numColors) , fBytesPerColor(bytesPerColor) , fOffset(offset) , fSwizzler(nullptr) - , fSrcRowBytes(SkAlign4(compute_row_bytes(this->getInfo().width(), this->bitsPerPixel()))) - , fSrcBuffer(new uint8_t [fSrcRowBytes]) + , fSrcBuffer(new uint8_t [this->srcRowBytes()]) + , fIsOpaque(isOpaque) , fInIco(inIco) , fAndMaskRowBytes(fInIco ? SkAlign4(compute_row_bytes(this->getInfo().width(), 1)) : 0) {} @@ -67,7 +68,7 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, /* * Process the color table for the bmp input */ - bool SkBmpStandardCodec::createColorTable(SkAlphaType alphaType, int* numColors) { + bool SkBmpStandardCodec::createColorTable(SkAlphaType dstAlphaType, int* numColors) { // Allocate memory for color table uint32_t colorBytes = 0; SkPMColor colorTable[256]; @@ -94,21 +95,10 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, // Choose the proper packing function SkPMColor (*packARGB) (uint32_t, uint32_t, uint32_t, uint32_t); - switch (alphaType) { - case kOpaque_SkAlphaType: - case kUnpremul_SkAlphaType: - packARGB = &SkPackARGB32NoCheck; - break; - case kPremul_SkAlphaType: - packARGB = &SkPreMultiplyARGB; - break; - default: - // This should not be reached because conversion possible - // should fail if the alpha type is not one of the above - // values. - SkASSERT(false); - packARGB = nullptr; - break; + if (fIsOpaque || kUnpremul_SkAlphaType == dstAlphaType) { + packARGB = &SkPackARGB32NoCheck; + } else { + packARGB = &SkPremultiplyARGBInline; } // Fill in the color table @@ -118,7 +108,7 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, uint8_t green = get_byte(cBuffer.get(), i*fBytesPerColor + 1); uint8_t red = get_byte(cBuffer.get(), i*fBytesPerColor + 2); uint8_t alpha; - if (kOpaque_SkAlphaType == alphaType) { + if (fIsOpaque) { alpha = 0xFF; } else { alpha = get_byte(cBuffer.get(), i*fBytesPerColor + 3); @@ -162,9 +152,9 @@ SkCodec::Result SkBmpStandardCodec::onGetPixels(const SkImageInfo& dstInfo, return true; } -bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) { +void SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) { // Get swizzler configuration - SkSwizzler::SrcConfig config; + SkSwizzler::SrcConfig config = SkSwizzler::kUnknown; switch (this->bitsPerPixel()) { case 1: config = SkSwizzler::kIndex1; @@ -182,7 +172,7 @@ bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Op config = SkSwizzler::kBGR; break; case 32: - if (kOpaque_SkAlphaType == dstInfo.alphaType()) { + if (fIsOpaque) { config = SkSwizzler::kBGRX; } else { config = SkSwizzler::kBGRA; @@ -190,7 +180,6 @@ bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Op break; default: SkASSERT(false); - return false; } // Get a pointer to the color table if it exists @@ -198,11 +187,7 @@ bool SkBmpStandardCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Op // Create swizzler fSwizzler.reset(SkSwizzler::CreateSwizzler(config, colorPtr, dstInfo, opts)); - - if (nullptr == fSwizzler.get()) { - return false; - } - return true; + SkASSERT(fSwizzler); } SkCodec::Result SkBmpStandardCodec::prepareToDecode(const SkImageInfo& dstInfo, @@ -217,11 +202,8 @@ SkCodec::Result SkBmpStandardCodec::prepareToDecode(const SkImageInfo& dstInfo, // Copy the color table to the client if necessary copy_color_table(dstInfo, this->fColorTable, inputColorPtr, inputColorCount); - // Initialize a swizzler if necessary - if (!this->initializeSwizzler(dstInfo, options)) { - SkCodecPrintf("Error: cannot initialize swizzler.\n"); - return SkCodec::kInvalidConversion; - } + // Initialize a swizzler + this->initializeSwizzler(dstInfo, options); return SkCodec::kSuccess; } @@ -234,7 +216,7 @@ int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t const int height = dstInfo.height(); for (int y = 0; y < height; y++) { // Read a row of the input - if (this->stream()->read(fSrcBuffer.get(), fSrcRowBytes) != fSrcRowBytes) { + if (this->stream()->read(fSrcBuffer.get(), this->srcRowBytes()) != this->srcRowBytes()) { SkCodecPrintf("Warning: incomplete input stream.\n"); return y; } @@ -246,7 +228,7 @@ int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t fSwizzler->swizzle(dstRow, fSrcBuffer.get()); } - if (fInIco) { + if (fInIco && fIsOpaque) { const int startScanline = this->currScanline(); if (startScanline < 0) { // We are not performing a scanline decode. @@ -271,7 +253,7 @@ int SkBmpStandardCodec::decodeRows(const SkImageInfo& dstInfo, void* dst, size_t // Calculate how many bytes we must skip to reach the AND mask. const int remainingScanlines = this->getInfo().height() - startScanline - height; - const size_t bytesToSkip = remainingScanlines * fSrcRowBytes + + const size_t bytesToSkip = remainingScanlines * this->srcRowBytes() + startScanline * fAndMaskRowBytes; const size_t subStreamStartPosition = currPosition + bytesToSkip; if (subStreamStartPosition >= length) { @@ -337,10 +319,10 @@ void SkBmpStandardCodec::decodeIcoMask(SkStream* stream, const SkImageInfo& dstI } } -uint32_t SkBmpStandardCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const { +uint32_t SkBmpStandardCodec::onGetFillValue(SkColorType colorType) const { const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); if (colorPtr) { return get_color_table_fill_value(colorType, colorPtr, 0); } - return INHERITED::onGetFillValue(colorType, alphaType); + return INHERITED::onGetFillValue(colorType); } diff --git a/gfx/skia/skia/src/codec/SkBmpStandardCodec.h b/gfx/skia/skia/src/codec/SkBmpStandardCodec.h index b7999001e1..557f6535c9 100644 --- a/gfx/skia/skia/src/codec/SkBmpStandardCodec.h +++ b/gfx/skia/skia/src/codec/SkBmpStandardCodec.h @@ -27,18 +27,20 @@ public: * @param srcInfo contains the source width and height * @param stream the stream of encoded image data * @param bitsPerPixel the number of bits used to store each pixel - * @param format the format of the bmp file * @param numColors the number of colors in the color table * @param bytesPerColor the number of bytes in the stream used to represent each color in the color table * @param offset the offset of the image pixel data from the end of the * headers * @param rowOrder indicates whether rows are ordered top-down or bottom-up + * @param isOpaque indicates if the bmp itself is opaque (before applying + * the icp mask, if there is one) + * @param inIco indicates if the bmp is embedded in an ico file */ SkBmpStandardCodec(const SkImageInfo& srcInfo, SkStream* stream, uint16_t bitsPerPixel, uint32_t numColors, uint32_t bytesPerColor, - uint32_t offset, SkCodec::SkScanlineOrder rowOrder, - bool isIco); + uint32_t offset, SkCodec::SkScanlineOrder rowOrder, bool isOpaque, + bool inIco); protected: @@ -55,7 +57,7 @@ protected: int* inputColorCount) override; - uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override; + uint32_t onGetFillValue(SkColorType) const override; SkSampler* getSampler(bool createIfNecessary) override { SkASSERT(fSwizzler); @@ -70,7 +72,7 @@ private: */ bool createColorTable(SkAlphaType alphaType, int* colorCount); - bool initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts); + void initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts); int decodeRows(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options& opts) override; @@ -88,8 +90,8 @@ private: const uint32_t fBytesPerColor; const uint32_t fOffset; SkAutoTDelete fSwizzler; - const size_t fSrcRowBytes; SkAutoTDeleteArray fSrcBuffer; + const bool fIsOpaque; const bool fInIco; const size_t fAndMaskRowBytes; // only used for fInIco decodes diff --git a/gfx/skia/skia/src/codec/SkCodec.cpp b/gfx/skia/skia/src/codec/SkCodec.cpp index 04ee7c6a43..7bc831a6ac 100644 --- a/gfx/skia/skia/src/codec/SkCodec.cpp +++ b/gfx/skia/skia/src/codec/SkCodec.cpp @@ -7,14 +7,16 @@ #include "SkBmpCodec.h" #include "SkCodec.h" -#include "SkCodec_libpng.h" #include "SkCodecPriv.h" +#include "SkColorSpace.h" #include "SkData.h" #include "SkGifCodec.h" #include "SkIcoCodec.h" -#if !defined(GOOGLE3) #include "SkJpegCodec.h" +#ifdef SK_CODEC_DECODES_PNG +#include "SkPngCodec.h" #endif +#include "SkRawCodec.h" #include "SkStream.h" #include "SkWbmpCodec.h" #include "SkWebpCodec.h" @@ -25,12 +27,18 @@ struct DecoderProc { }; static const DecoderProc gDecoderProcs[] = { -#if !defined(GOOGLE3) +#ifdef SK_CODEC_DECODES_JPEG { SkJpegCodec::IsJpeg, SkJpegCodec::NewFromStream }, #endif +#ifdef SK_CODEC_DECODES_WEBP { SkWebpCodec::IsWebp, SkWebpCodec::NewFromStream }, +#endif +#ifdef SK_CODEC_DECODES_GIF { SkGifCodec::IsGif, SkGifCodec::NewFromStream }, +#endif +#ifdef SK_CODEC_DECODES_PNG { SkIcoCodec::IsIco, SkIcoCodec::NewFromStream }, +#endif { SkBmpCodec::IsBmp, SkBmpCodec::NewFromStream }, { SkWbmpCodec::IsWbmp, SkWbmpCodec::NewFromStream } }; @@ -78,14 +86,22 @@ SkCodec* SkCodec::NewFromStream(SkStream* stream, // PNG is special, since we want to be able to supply an SkPngChunkReader. // But this code follows the same pattern as the loop. +#ifdef SK_CODEC_DECODES_PNG if (SkPngCodec::IsPng(buffer, bytesRead)) { - return SkPngCodec::NewFromStream(streamDeleter.detach(), chunkReader); - } else { + return SkPngCodec::NewFromStream(streamDeleter.release(), chunkReader); + } else +#endif + { for (DecoderProc proc : gDecoderProcs) { if (proc.IsFormat(buffer, bytesRead)) { - return proc.NewFromStream(streamDeleter.detach()); + return proc.NewFromStream(streamDeleter.release()); } } + +#ifdef SK_CODEC_DECODES_RAW + // Try to treat the input as RAW if all the other checks failed. + return SkRawCodec::NewFromStream(streamDeleter.release()); +#endif } return nullptr; @@ -98,10 +114,13 @@ SkCodec* SkCodec::NewFromData(SkData* data, SkPngChunkReader* reader) { return NewFromStream(new SkMemoryStream(data), reader); } -SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream) +SkCodec::SkCodec(const SkImageInfo& info, SkStream* stream, sk_sp colorSpace, + Origin origin) : fSrcInfo(info) , fStream(stream) , fNeedsRewind(false) + , fColorSpace(colorSpace) + , fOrigin(origin) , fDstInfo() , fOptions() , fCurrScanline(-1) @@ -158,15 +177,6 @@ SkCodec::Result SkCodec::getPixels(const SkImageInfo& info, void* pixels, size_t ctable = nullptr; } - { - SkAlphaType canonical; - if (!SkColorTypeValidateAlphaType(info.colorType(), info.alphaType(), &canonical) - || canonical != info.alphaType()) - { - return kInvalidConversion; - } - } - if (!this->rewindIfNeeded()) { return kCouldNotRewind; } @@ -344,27 +354,32 @@ void SkCodec::fillIncompleteImage(const SkImageInfo& info, void* dst, size_t row ZeroInitialized zeroInit, int linesRequested, int linesDecoded) { void* fillDst; - const uint32_t fillValue = this->getFillValue(info.colorType(), info.alphaType()); + const uint32_t fillValue = this->getFillValue(info.colorType()); const int linesRemaining = linesRequested - linesDecoded; SkSampler* sampler = this->getSampler(false); + int fillWidth = info.width(); + if (fOptions.fSubset) { + fillWidth = fOptions.fSubset->width(); + } + switch (this->getScanlineOrder()) { case kTopDown_SkScanlineOrder: case kNone_SkScanlineOrder: { - const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining); + const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining); fillDst = SkTAddOffset(dst, linesDecoded * rowBytes); fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); break; } case kBottomUp_SkScanlineOrder: { fillDst = dst; - const SkImageInfo fillInfo = info.makeWH(info.width(), linesRemaining); + const SkImageInfo fillInfo = info.makeWH(fillWidth, linesRemaining); fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); break; } case kOutOfOrder_SkScanlineOrder: { SkASSERT(1 == linesRequested || this->getInfo().height() == linesRequested); - const SkImageInfo fillInfo = info.makeWH(info.width(), 1); + const SkImageInfo fillInfo = info.makeWH(fillWidth, 1); for (int srcY = linesDecoded; srcY < linesRequested; srcY++) { fillDst = SkTAddOffset(dst, this->outputScanline(srcY) * rowBytes); fill_proc(fillInfo, fillDst, rowBytes, fillValue, zeroInit, sampler); diff --git a/gfx/skia/skia/src/codec/SkCodecImageGenerator.cpp b/gfx/skia/skia/src/codec/SkCodecImageGenerator.cpp index 2fef381ec1..db13aaea29 100644 --- a/gfx/skia/skia/src/codec/SkCodecImageGenerator.cpp +++ b/gfx/skia/skia/src/codec/SkCodecImageGenerator.cpp @@ -16,8 +16,24 @@ SkImageGenerator* SkCodecImageGenerator::NewFromEncodedCodec(SkData* data) { return new SkCodecImageGenerator(codec, data); } +// FIXME: We should expose information about the encoded format on the +// SkImageGenerator, so the client can interpret the encoded +// format and request an output format. For now, as a workaround, +// we guess what output format the client wants. +static SkImageInfo fix_info(const SkCodec& codec) { + const SkImageInfo& info = codec.getInfo(); + SkAlphaType alphaType = (kUnpremul_SkAlphaType == info.alphaType()) ? kPremul_SkAlphaType : + info.alphaType(); + + // Crudely guess that the presence of a color space means sRGB. + SkColorProfileType profileType = (codec.getColorSpace()) ? kSRGB_SkColorProfileType : + kLinear_SkColorProfileType; + + return SkImageInfo::Make(info.width(), info.height(), info.colorType(), alphaType, profileType); +} + SkCodecImageGenerator::SkCodecImageGenerator(SkCodec* codec, SkData* data) - : INHERITED(codec->getInfo()) + : INHERITED(fix_info(*codec)) , fCodec(codec) , fData(SkRef(data)) {} @@ -40,7 +56,19 @@ bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& info, void* pixels, s } } -bool SkCodecImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) { - return false; +bool SkCodecImageGenerator::onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const +{ + return fCodec->queryYUV8(sizeInfo, colorSpace); +} + +bool SkCodecImageGenerator::onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) { + SkCodec::Result result = fCodec->getYUV8Planes(sizeInfo, planes); + + switch (result) { + case SkCodec::kSuccess: + case SkCodec::kIncompleteInput: + return true; + default: + return false; + } } diff --git a/gfx/skia/skia/src/codec/SkCodecImageGenerator.h b/gfx/skia/skia/src/codec/SkCodecImageGenerator.h index 80eacb19c8..6d34223110 100644 --- a/gfx/skia/skia/src/codec/SkCodecImageGenerator.h +++ b/gfx/skia/skia/src/codec/SkCodecImageGenerator.h @@ -26,8 +26,9 @@ protected: bool onGetPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, SkPMColor ctable[], int* ctableCount) override; - bool onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) override; + bool onQueryYUV8(SkYUVSizeInfo*, SkYUVColorSpace*) const override; + + bool onGetYUV8Planes(const SkYUVSizeInfo&, void* planes[3]) override; private: /* diff --git a/gfx/skia/skia/src/codec/SkCodecPriv.h b/gfx/skia/skia/src/codec/SkCodecPriv.h index 27e2a63722..8dde60fcd3 100644 --- a/gfx/skia/skia/src/codec/SkCodecPriv.h +++ b/gfx/skia/skia/src/codec/SkCodecPriv.h @@ -12,7 +12,12 @@ #include "SkColorTable.h" #include "SkImageInfo.h" #include "SkTypes.h" -#include "SkUtils.h" + +#ifdef SK_PRINT_CODEC_MESSAGES + #define SkCodecPrintf SkDebugf +#else + #define SkCodecPrintf(...) +#endif // FIXME: Consider sharing with dm, nanbench, and tools. inline float get_scale_from_sample_size(int sampleSize) { @@ -75,11 +80,16 @@ inline bool is_coord_necessary(int srcCoord, int sampleFactor, int scaledDim) { } inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) { - // Check for supported alpha types + if (kUnknown_SkAlphaType == dstAlpha) { + return false; + } + if (srcAlpha != dstAlpha) { if (kOpaque_SkAlphaType == srcAlpha) { - // If the source is opaque, we must decode to opaque - return false; + // If the source is opaque, we can support any. + SkCodecPrintf("Warning: an opaque image should be decoded as opaque " + "- it is being decoded as non-opaque, which will draw slower\n"); + return true; } // The source is not opaque @@ -99,15 +109,17 @@ inline bool valid_alpha(SkAlphaType dstAlpha, SkAlphaType srcAlpha) { /* * Most of our codecs support the same conversions: * - profileType must be the same - * - opaque only to opaque (and 565 only if opaque) + * - opaque to any alpha type + * - 565 only if opaque * - premul to unpremul and vice versa * - always support N32 * - otherwise match the src color type */ inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) { - if (dst.profileType() != src.profileType()) { - return false; - } + // FIXME: skbug.com/4895 + // Currently, we ignore the SkColorProfileType on the SkImageInfo. We + // will treat the encoded data as linear regardless of what the client + // requests. // Ensure the alpha type is valid if (!valid_alpha(dst.alphaType(), src.alphaType())) { @@ -119,7 +131,12 @@ inline bool conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) case kN32_SkColorType: return true; case kRGB_565_SkColorType: - return src.alphaType() == kOpaque_SkAlphaType; + return kOpaque_SkAlphaType == dst.alphaType(); + case kGray_8_SkColorType: + if (kOpaque_SkAlphaType != dst.alphaType()) { + return false; + } + // Fall through default: return dst.colorType() == src.colorType(); } @@ -230,10 +247,28 @@ inline uint32_t get_int(uint8_t* buffer, uint32_t i) { #endif } -#ifdef SK_PRINT_CODEC_MESSAGES - #define SkCodecPrintf SkDebugf -#else - #define SkCodecPrintf(...) -#endif +/* + * @param data Buffer to read bytes from + * @param isLittleEndian Output parameter + * Indicates if the data is little endian + * Is unaffected on false returns + */ +inline bool is_valid_endian_marker(const uint8_t* data, bool* isLittleEndian) { + // II indicates Intel (little endian) and MM indicates motorola (big endian). + if (('I' != data[0] || 'I' != data[1]) && ('M' != data[0] || 'M' != data[1])) { + return false; + } + + *isLittleEndian = ('I' == data[0]); + return true; +} + +inline uint16_t get_endian_short(const uint8_t* data, bool littleEndian) { + if (littleEndian) { + return (data[1] << 8) | (data[0]); + } + + return (data[0] << 8) | (data[1]); +} #endif // SkCodecPriv_DEFINED diff --git a/gfx/skia/skia/src/codec/SkGifCodec.cpp b/gfx/skia/skia/src/codec/SkGifCodec.cpp index 92470bf3a0..2233c66c19 100644 --- a/gfx/skia/skia/src/codec/SkGifCodec.cpp +++ b/gfx/skia/skia/src/codec/SkGifCodec.cpp @@ -13,6 +13,8 @@ #include "SkSwizzler.h" #include "SkUtils.h" +#include "gif_lib.h" + /* * Checks the start of the stream to see if the image is a gif */ @@ -49,7 +51,11 @@ static int32_t read_bytes_callback(GifFileType* fileType, GifByteType* out, int3 * Open the gif file */ static GifFileType* open_gif(SkStream* stream) { +#if GIFLIB_MAJOR < 5 + return DGifOpen(stream, read_bytes_callback); +#else return DGifOpen(stream, read_bytes_callback, nullptr); +#endif } /* @@ -117,7 +123,11 @@ inline uint32_t get_output_row_interlaced(uint32_t encodedRow, uint32_t height) * It is used in a SkAutoTCallIProc template */ void SkGifCodec::CloseGif(GifFileType* gif) { - DGifCloseFile(gif, NULL); +#if GIFLIB_MAJOR < 5 || (GIFLIB_MAJOR == 5 && GIFLIB_MINOR == 0) + DGifCloseFile(gif); +#else + DGifCloseFile(gif, nullptr); +#endif } /* @@ -126,7 +136,11 @@ void SkGifCodec::CloseGif(GifFileType* gif) { */ void SkGifCodec::FreeExtension(SavedImage* image) { if (NULL != image->ExtensionBlocks) { +#if GIFLIB_MAJOR < 5 + FreeExtension(image); +#else GifFreeExtensions(&image->ExtensionBlockCount, &image->ExtensionBlocks); +#endif } } @@ -202,12 +216,12 @@ bool SkGifCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, GifFileType** // the default. SkImageInfo imageInfo = SkImageInfo::Make(size.width(), size.height(), kIndex_8_SkColorType, alphaType); - *codecOut = new SkGifCodec(imageInfo, streamDeleter.detach(), gif.detach(), transIndex, + *codecOut = new SkGifCodec(imageInfo, streamDeleter.release(), gif.release(), transIndex, frameRect, frameIsSubset); } else { SkASSERT(nullptr != gifOut); - streamDeleter.detach(); - *gifOut = gif.detach(); + streamDeleter.release(); + *gifOut = gif.release(); } return true; } @@ -311,10 +325,15 @@ SkCodec::Result SkGifCodec::ReadUpToFirstImage(GifFileType* gif, uint32_t* trans // Create an extension block with our data while (nullptr != extData) { // Add a single block + +#if GIFLIB_MAJOR < 5 + if (AddExtensionBlock(&saveExt, extData[0], + &extData[1]) == GIF_ERROR) { +#else if (GIF_ERROR == GifAddExtensionBlock(&saveExt.ExtensionBlockCount, &saveExt.ExtensionBlocks, - extFunction, extData[0], &extData[1])) - { + extFunction, extData[0], &extData[1])) { +#endif return gif_error("Could not add extension block.\n", kIncompleteInput); } // Move to the next block @@ -399,26 +418,30 @@ void SkGifCodec::initializeColorTable(const SkImageInfo& dstInfo, SkPMColor* inp } } - // Gifs have the option to specify the color at a single index of the color - // table as transparent. If the transparent index is greater than the - // colorCount, we know that there is no valid transparent color in the color - // table. If there is not valid transparent index, we will try to use the - // backgroundIndex as the fill index. If the backgroundIndex is also not - // valid, we will let fFillIndex default to 0 (it is set to zero in the - // constructor). This behavior is not specified but matches - // SkImageDecoder_libgif. - uint32_t backgroundIndex = fGif->SBackGroundColor; - if (fTransIndex < colorCount) { - colorPtr[fTransIndex] = SK_ColorTRANSPARENT; - fFillIndex = fTransIndex; - } else if (backgroundIndex < colorCount) { - fFillIndex = backgroundIndex; - } - // Fill in the color table for indices greater than color count. // This allows for predictable, safe behavior. - for (uint32_t i = colorCount; i < maxColors; i++) { - colorPtr[i] = colorPtr[fFillIndex]; + if (colorCount > 0) { + // Gifs have the option to specify the color at a single index of the color + // table as transparent. If the transparent index is greater than the + // colorCount, we know that there is no valid transparent color in the color + // table. If there is not valid transparent index, we will try to use the + // backgroundIndex as the fill index. If the backgroundIndex is also not + // valid, we will let fFillIndex default to 0 (it is set to zero in the + // constructor). This behavior is not specified but matches + // SkImageDecoder_libgif. + uint32_t backgroundIndex = fGif->SBackGroundColor; + if (fTransIndex < colorCount) { + colorPtr[fTransIndex] = SK_ColorTRANSPARENT; + fFillIndex = fTransIndex; + } else if (backgroundIndex < colorCount) { + fFillIndex = backgroundIndex; + } + + for (uint32_t i = colorCount; i < maxColors; i++) { + colorPtr[i] = colorPtr[fFillIndex]; + } + } else { + sk_memset32(colorPtr, 0xFF000000, maxColors); } fColorTable.reset(new SkColorTable(colorPtr, maxColors)); @@ -436,19 +459,16 @@ SkCodec::Result SkGifCodec::prepareToDecode(const SkImageInfo& dstInfo, SkPMColo // Initialize color table and copy to the client if necessary this->initializeColorTable(dstInfo, inputColorPtr, inputColorCount); - return this->initializeSwizzler(dstInfo, opts); + this->initializeSwizzler(dstInfo, opts); + return kSuccess; } -SkCodec::Result SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) { +void SkGifCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& opts) { const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); const SkIRect* frameRect = fFrameIsSubset ? &fFrameRect : nullptr; fSwizzler.reset(SkSwizzler::CreateSwizzler(SkSwizzler::kIndex, colorPtr, dstInfo, opts, frameRect)); - - if (nullptr != fSwizzler.get()) { - return kSuccess; - } - return kUnimplemented; + SkASSERT(fSwizzler); } bool SkGifCodec::readRow() { @@ -476,8 +496,7 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, // Initialize the swizzler if (fFrameIsSubset) { // Fill the background - SkSampler::Fill(dstInfo, dst, dstRowBytes, - this->getFillValue(dstInfo.colorType(), dstInfo.alphaType()), + SkSampler::Fill(dstInfo, dst, dstRowBytes, this->getFillValue(dstInfo.colorType()), opts.fZeroInitialized); } @@ -495,7 +514,7 @@ SkCodec::Result SkGifCodec::onGetPixels(const SkImageInfo& dstInfo, // FIXME: This is similar to the implementation for bmp and png. Can we share more code or // possibly make this non-virtual? -uint32_t SkGifCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const { +uint32_t SkGifCodec::onGetFillValue(SkColorType colorType) const { const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); return get_color_table_fill_value(colorType, colorPtr, fFillIndex); } @@ -538,8 +557,7 @@ int SkGifCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { if (fFrameIsSubset) { // Fill the requested rows SkImageInfo fillInfo = this->dstInfo().makeWH(this->dstInfo().width(), count); - uint32_t fillValue = this->onGetFillValue(this->dstInfo().colorType(), - this->dstInfo().alphaType()); + uint32_t fillValue = this->onGetFillValue(this->dstInfo().colorType()); fSwizzler->fill(fillInfo, dst, rowBytes, fillValue, this->options().fZeroInitialized); // Start to write pixels at the start of the image frame diff --git a/gfx/skia/skia/src/codec/SkGifCodec.h b/gfx/skia/skia/src/codec/SkGifCodec.h index ba48989cbb..a08e7ee552 100644 --- a/gfx/skia/skia/src/codec/SkGifCodec.h +++ b/gfx/skia/skia/src/codec/SkGifCodec.h @@ -6,11 +6,13 @@ */ #include "SkCodec.h" +#include "SkColorSpace.h" #include "SkColorTable.h" #include "SkImageInfo.h" #include "SkSwizzler.h" -#include "gif_lib.h" +struct GifFileType; +struct SavedImage; /* * @@ -64,7 +66,7 @@ protected: bool onRewind() override; - uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override; + uint32_t onGetFillValue(SkColorType) const override; int onOutputScanline(int inputScanline) const override; @@ -128,7 +130,7 @@ private: * @param options Informs the swizzler if destination memory is zero initialized. * Contains subset information. */ - Result initializeSwizzler(const SkImageInfo& dstInfo, + void initializeSwizzler(const SkImageInfo& dstInfo, const Options& options); SkSampler* getSampler(bool createIfNecessary) override { diff --git a/gfx/skia/skia/src/codec/SkIcoCodec.cpp b/gfx/skia/skia/src/codec/SkIcoCodec.cpp index 0280be304c..d74c150ff5 100644 --- a/gfx/skia/skia/src/codec/SkIcoCodec.cpp +++ b/gfx/skia/skia/src/codec/SkIcoCodec.cpp @@ -6,52 +6,15 @@ */ #include "SkBmpCodec.h" -#include "SkCodec_libpng.h" #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkData.h" #include "SkIcoCodec.h" +#include "SkPngCodec.h" #include "SkStream.h" #include "SkTDArray.h" #include "SkTSort.h" -static bool ico_conversion_possible(const SkImageInfo& dstInfo) { - // We only support kN32_SkColorType. - // This makes sense for BMP-in-ICO. The presence of an AND - // mask (which changes colors and adds transparency) means that - // we cannot use k565 or kIndex8. - // FIXME: For PNG-in-ICO, we could technically support whichever - // color types that the png supports. - if (kN32_SkColorType != dstInfo.colorType()) { - return false; - } - - // We only support transparent alpha types. This is necessary for - // BMP-in-ICOs since there will be an AND mask. - // FIXME: For opaque PNG-in-ICOs, we should be able to support kOpaque. - return kPremul_SkAlphaType == dstInfo.alphaType() || - kUnpremul_SkAlphaType == dstInfo.alphaType(); -} - -static SkImageInfo fix_embedded_alpha(const SkImageInfo& dstInfo, SkAlphaType embeddedAlpha) { - // FIXME (msarett): ICO is considered non-opaque, even if the embedded BMP - // incorrectly claims it has no alpha. - switch (embeddedAlpha) { - case kPremul_SkAlphaType: - case kUnpremul_SkAlphaType: - // Use the requested alpha type if the embedded codec supports alpha. - embeddedAlpha = dstInfo.alphaType(); - break; - case kOpaque_SkAlphaType: - // If the embedded codec claims it is opaque, decode as if it is opaque. - break; - default: - SkASSERT(false); - break; - } - return dstInfo.makeAlphaType(embeddedAlpha); -} - /* * Checks the start of the stream to see if the image is an Ico or Cur */ @@ -177,9 +140,9 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) { // Check if the embedded codec is bmp or png and create the codec SkCodec* codec = nullptr; if (SkPngCodec::IsPng((const char*) data->bytes(), data->size())) { - codec = SkPngCodec::NewFromStream(embeddedStream.detach()); + codec = SkPngCodec::NewFromStream(embeddedStream.release()); } else { - codec = SkBmpCodec::NewFromIco(embeddedStream.detach()); + codec = SkBmpCodec::NewFromIco(embeddedStream.release()); } // Save a valid codec @@ -207,20 +170,9 @@ SkCodec* SkIcoCodec::NewFromStream(SkStream* stream) { } SkImageInfo info = codecs->operator[](maxIndex)->getInfo(); - // ICOs contain an alpha mask after the image which means we cannot - // guarantee that an image is opaque, even if the sub-codec thinks it - // is. - // FIXME (msarett): The BMP decoder depends on the alpha type in order - // to decode correctly, otherwise it could report kUnpremul and we would - // not have to correct it here. Is there a better way? - // FIXME (msarett): This is only true for BMP in ICO - could a PNG in ICO - // be opaque? Is it okay that we missed out on the opportunity to mark - // such an image as opaque? - info = info.makeAlphaType(kUnpremul_SkAlphaType); - // Note that stream is owned by the embedded codec, the ico does not need // direct access to the stream. - return new SkIcoCodec(info, codecs.detach()); + return new SkIcoCodec(info, codecs.release()); } /* @@ -290,10 +242,6 @@ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, return kUnimplemented; } - if (!ico_conversion_possible(dstInfo)) { - return kInvalidConversion; - } - int index = 0; SkCodec::Result result = kInvalidScale; while (true) { @@ -303,9 +251,7 @@ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, } SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index); - SkImageInfo decodeInfo = fix_embedded_alpha(dstInfo, embeddedCodec->getInfo().alphaType()); - SkASSERT(decodeInfo.colorType() == kN32_SkColorType); - result = embeddedCodec->getPixels(decodeInfo, dst, dstRowBytes, &opts, colorTable, + result = embeddedCodec->getPixels(dstInfo, dst, dstRowBytes, &opts, colorTable, colorCount); switch (result) { @@ -313,7 +259,7 @@ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, case kIncompleteInput: // The embedded codec will handle filling incomplete images, so we will indicate // that all of the rows are initialized. - *rowsDecoded = decodeInfo.height(); + *rowsDecoded = dstInfo.height(); return result; default: // Continue trying to find a valid embedded codec on a failed decode. @@ -329,10 +275,6 @@ SkCodec::Result SkIcoCodec::onGetPixels(const SkImageInfo& dstInfo, SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const SkCodec::Options& options, SkPMColor colorTable[], int* colorCount) { - if (!ico_conversion_possible(dstInfo)) { - return kInvalidConversion; - } - int index = 0; SkCodec::Result result = kInvalidScale; while (true) { @@ -342,8 +284,7 @@ SkCodec::Result SkIcoCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, } SkCodec* embeddedCodec = fEmbeddedCodecs->operator[](index); - SkImageInfo decodeInfo = fix_embedded_alpha(dstInfo, embeddedCodec->getInfo().alphaType()); - result = embeddedCodec->startScanlineDecode(decodeInfo, &options, colorTable, colorCount); + result = embeddedCodec->startScanlineDecode(dstInfo, &options, colorTable, colorCount); if (kSuccess == result) { fCurrScanlineCodec = embeddedCodec; return result; diff --git a/gfx/skia/skia/src/codec/SkJpegCodec.cpp b/gfx/skia/skia/src/codec/SkJpegCodec.cpp index 7db772da63..76d2ee8572 100644 --- a/gfx/skia/skia/src/codec/SkJpegCodec.cpp +++ b/gfx/skia/skia/src/codec/SkJpegCodec.cpp @@ -6,9 +6,9 @@ */ #include "SkCodec.h" +#include "SkMSAN.h" #include "SkJpegCodec.h" #include "SkJpegDecoderMgr.h" -#include "SkJpegUtility_codec.h" #include "SkCodecPriv.h" #include "SkColorPriv.h" #include "SkStream.h" @@ -17,6 +17,7 @@ // stdio is needed for libjpeg-turbo #include +#include "SkJpegUtility.h" extern "C" { #include "jerror.h" @@ -28,6 +29,160 @@ bool SkJpegCodec::IsJpeg(const void* buffer, size_t bytesRead) { return bytesRead >= 3 && !memcmp(buffer, jpegSig, sizeof(jpegSig)); } +static uint32_t get_endian_int(const uint8_t* data, bool littleEndian) { + if (littleEndian) { + return (data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data[0]); + } + + return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | (data[3]); +} + +const uint32_t kExifHeaderSize = 14; +const uint32_t kICCHeaderSize = 14; +const uint32_t kExifMarker = JPEG_APP0 + 1; +const uint32_t kICCMarker = JPEG_APP0 + 2; + +static bool is_orientation_marker(jpeg_marker_struct* marker, SkCodec::Origin* orientation) { + if (kExifMarker != marker->marker || marker->data_length < kExifHeaderSize) { + return false; + } + + const uint8_t* data = marker->data; + static const uint8_t kExifSig[] { 'E', 'x', 'i', 'f', '\0' }; + if (memcmp(data, kExifSig, sizeof(kExifSig))) { + return false; + } + + bool littleEndian; + if (!is_valid_endian_marker(data + 6, &littleEndian)) { + return false; + } + + // Get the offset from the start of the marker. + // Account for 'E', 'x', 'i', 'f', '\0', ''. + uint32_t offset = get_endian_int(data + 10, littleEndian); + offset += sizeof(kExifSig) + 1; + + // Require that the marker is at least large enough to contain the number of entries. + if (marker->data_length < offset + 2) { + return false; + } + uint32_t numEntries = get_endian_short(data + offset, littleEndian); + + // Tag (2 bytes), Datatype (2 bytes), Number of elements (4 bytes), Data (4 bytes) + const uint32_t kEntrySize = 12; + numEntries = SkTMin(numEntries, (marker->data_length - offset - 2) / kEntrySize); + + // Advance the data to the start of the entries. + data += offset + 2; + + const uint16_t kOriginTag = 0x112; + const uint16_t kOriginType = 3; + for (uint32_t i = 0; i < numEntries; i++, data += kEntrySize) { + uint16_t tag = get_endian_short(data, littleEndian); + uint16_t type = get_endian_short(data + 2, littleEndian); + uint32_t count = get_endian_int(data + 4, littleEndian); + if (kOriginTag == tag && kOriginType == type && 1 == count) { + uint16_t val = get_endian_short(data + 8, littleEndian); + if (0 < val && val <= SkCodec::kLast_Origin) { + *orientation = (SkCodec::Origin) val; + return true; + } + } + } + + return false; +} + +static SkCodec::Origin get_exif_orientation(jpeg_decompress_struct* dinfo) { + SkCodec::Origin orientation; + for (jpeg_marker_struct* marker = dinfo->marker_list; marker; marker = marker->next) { + if (is_orientation_marker(marker, &orientation)) { + return orientation; + } + } + + return SkCodec::kDefault_Origin; +} + +static bool is_icc_marker(jpeg_marker_struct* marker) { + if (kICCMarker != marker->marker || marker->data_length < kICCHeaderSize) { + return false; + } + + static const uint8_t kICCSig[] { 'I', 'C', 'C', '_', 'P', 'R', 'O', 'F', 'I', 'L', 'E', '\0' }; + return !memcmp(marker->data, kICCSig, sizeof(kICCSig)); +} + +/* + * ICC profiles may be stored using a sequence of multiple markers. We obtain the ICC profile + * in two steps: + * (1) Discover all ICC profile markers and verify that they are numbered properly. + * (2) Copy the data from each marker into a contiguous ICC profile. + */ +static sk_sp get_icc_profile(jpeg_decompress_struct* dinfo) { + // Note that 256 will be enough storage space since each markerIndex is stored in 8-bits. + jpeg_marker_struct* markerSequence[256]; + memset(markerSequence, 0, sizeof(markerSequence)); + uint8_t numMarkers = 0; + size_t totalBytes = 0; + + // Discover any ICC markers and verify that they are numbered properly. + for (jpeg_marker_struct* marker = dinfo->marker_list; marker; marker = marker->next) { + if (is_icc_marker(marker)) { + // Verify that numMarkers is valid and consistent. + if (0 == numMarkers) { + numMarkers = marker->data[13]; + if (0 == numMarkers) { + SkCodecPrintf("ICC Profile Error: numMarkers must be greater than zero.\n"); + return nullptr; + } + } else if (numMarkers != marker->data[13]) { + SkCodecPrintf("ICC Profile Error: numMarkers must be consistent.\n"); + return nullptr; + } + + // Verify that the markerIndex is valid and unique. Note that zero is not + // a valid index. + uint8_t markerIndex = marker->data[12]; + if (markerIndex == 0 || markerIndex > numMarkers) { + SkCodecPrintf("ICC Profile Error: markerIndex is invalid.\n"); + return nullptr; + } + if (markerSequence[markerIndex]) { + SkCodecPrintf("ICC Profile Error: Duplicate value of markerIndex.\n"); + return nullptr; + } + markerSequence[markerIndex] = marker; + SkASSERT(marker->data_length >= kICCHeaderSize); + totalBytes += marker->data_length - kICCHeaderSize; + } + } + + if (0 == totalBytes) { + // No non-empty ICC profile markers were found. + return nullptr; + } + + // Combine the ICC marker data into a contiguous profile. + SkAutoMalloc iccData(totalBytes); + void* dst = iccData.get(); + for (uint32_t i = 1; i <= numMarkers; i++) { + jpeg_marker_struct* marker = markerSequence[i]; + if (!marker) { + SkCodecPrintf("ICC Profile Error: Missing marker %d of %d.\n", i, numMarkers); + return nullptr; + } + + void* src = SkTAddOffset(marker->data, kICCHeaderSize); + size_t bytes = marker->data_length - kICCHeaderSize; + memcpy(dst, src, bytes); + dst = SkTAddOffset(dst, bytes); + } + + return SkColorSpace::NewICC(iccData.get(), totalBytes); +} + bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, JpegDecoderMgr** decoderMgrOut) { @@ -42,22 +197,35 @@ bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut, // Initialize the decompress info and the source manager decoderMgr->init(); + // Instruct jpeg library to save the markers that we care about. Since + // the orientation and color profile will not change, we can skip this + // step on rewinds. + if (codecOut) { + jpeg_save_markers(decoderMgr->dinfo(), kExifMarker, 0xFFFF); + jpeg_save_markers(decoderMgr->dinfo(), kICCMarker, 0xFFFF); + } + // Read the jpeg header if (JPEG_HEADER_OK != jpeg_read_header(decoderMgr->dinfo(), true)) { return decoderMgr->returnFalse("read_header"); } - if (nullptr != codecOut) { + if (codecOut) { // Recommend the color type to decode to const SkColorType colorType = decoderMgr->getColorType(); // Create image info object and the codec const SkImageInfo& imageInfo = SkImageInfo::Make(decoderMgr->dinfo()->image_width, decoderMgr->dinfo()->image_height, colorType, kOpaque_SkAlphaType); - *codecOut = new SkJpegCodec(imageInfo, stream, decoderMgr.detach()); + + Origin orientation = get_exif_orientation(decoderMgr->dinfo()); + sk_sp colorSpace = get_icc_profile(decoderMgr->dinfo()); + + *codecOut = new SkJpegCodec(imageInfo, stream, decoderMgr.release(), colorSpace, + orientation); } else { SkASSERT(nullptr != decoderMgrOut); - *decoderMgrOut = decoderMgr.detach(); + *decoderMgrOut = decoderMgr.release(); } return true; } @@ -68,24 +236,30 @@ SkCodec* SkJpegCodec::NewFromStream(SkStream* stream) { if (ReadHeader(stream, &codec, nullptr)) { // Codec has taken ownership of the stream, we do not need to delete it SkASSERT(codec); - streamDeleter.detach(); + streamDeleter.release(); return codec; } return nullptr; } SkJpegCodec::SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, - JpegDecoderMgr* decoderMgr) - : INHERITED(srcInfo, stream) + JpegDecoderMgr* decoderMgr, sk_sp colorSpace, Origin origin) + : INHERITED(srcInfo, stream, colorSpace, origin) , fDecoderMgr(decoderMgr) , fReadyState(decoderMgr->dinfo()->global_state) + , fSwizzlerSubset(SkIRect::MakeEmpty()) {} /* * Return the row bytes of a particular image type and width */ -static int get_row_bytes(const j_decompress_ptr dinfo) { - int colorBytes = (dinfo->out_color_space == JCS_RGB565) ? 2 : dinfo->out_color_components; +static size_t get_row_bytes(const j_decompress_ptr dinfo) { +#ifdef TURBO_HAS_565 + const size_t colorBytes = (dinfo->out_color_space == JCS_RGB565) ? 2 : + dinfo->out_color_components; +#else + const size_t colorBytes = dinfo->out_color_components; +#endif return dinfo->output_width * colorBytes; } @@ -157,16 +331,13 @@ bool SkJpegCodec::onRewind() { * Sets the output color space */ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) { - const SkImageInfo& src = this->getInfo(); - - // Ensure that the profile type is unchanged - if (dst.profileType() != src.profileType()) { + if (kUnknown_SkAlphaType == dst.alphaType()) { return false; } - // Ensure that the alpha type is opaque if (kOpaque_SkAlphaType != dst.alphaType()) { - return false; + SkCodecPrintf("Warning: an opaque image should be decoded as opaque " + "- it is being decoded as non-opaque, which will draw slower\n"); } // Check if we will decode to CMYK because a conversion to RGBA is not supported @@ -179,12 +350,16 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) { if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { - // Check the byte ordering of the RGBA color space for the - // current platform -#if defined(SK_PMCOLOR_IS_RGBA) - fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; +#ifdef LIBJPEG_TURBO_VERSION + // Check the byte ordering of the RGBA color space for the + // current platform + #ifdef SK_PMCOLOR_IS_RGBA + fDecoderMgr->dinfo()->out_color_space = JCS_EXT_RGBA; + #else + fDecoderMgr->dinfo()->out_color_space = JCS_EXT_BGRA; + #endif #else - fDecoderMgr->dinfo()->out_color_space = JCS_EXT_BGRA; + fDecoderMgr->dinfo()->out_color_space = JCS_RGB; #endif } return true; @@ -192,8 +367,12 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) { if (isCMYK) { fDecoderMgr->dinfo()->out_color_space = JCS_CMYK; } else { +#ifdef TURBO_HAS_565 fDecoderMgr->dinfo()->dither_mode = JDITHER_NONE; fDecoderMgr->dinfo()->out_color_space = JCS_RGB565; +#else + fDecoderMgr->dinfo()->out_color_space = JCS_RGB; +#endif } return true; case kGray_8_SkColorType: @@ -211,7 +390,7 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dst) { } /* - * Checks if we can natively scale to the requested dimensions and natively scales the + * Checks if we can natively scale to the requested dimensions and natively scales the * dimensions if possible */ bool SkJpegCodec::onDimensionsSupported(const SkISize& size) { @@ -285,7 +464,8 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, // If it's not, we want to know because it means our strategy is not optimal. SkASSERT(1 == dinfo->rec_outbuf_height); - if (JCS_CMYK == dinfo->out_color_space) { + J_COLOR_SPACE colorSpace = dinfo->out_color_space; + if (JCS_CMYK == colorSpace || JCS_RGB == colorSpace) { this->initializeSwizzler(dstInfo, options); } @@ -304,6 +484,7 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo, for (uint32_t y = 0; y < dstHeight; y++) { // Read rows of the image uint32_t lines = jpeg_read_scanlines(dinfo, &dstRow, 1); + sk_msan_mark_initialized(dstRow, dstRow + dstRowBytes, "skbug.com/4550"); // If we cannot read enough rows, assume the input is incomplete if (lines != 1) { @@ -329,18 +510,17 @@ void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& if (JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { srcConfig = SkSwizzler::kCMYK; } else { + // If the out_color_space is not CMYK, the only reason we would need a swizzler is + // for sampling and/or subsetting. switch (dstInfo.colorType()) { case kGray_8_SkColorType: - srcConfig = SkSwizzler::kGray; + srcConfig = SkSwizzler::kNoOp8; break; - case kRGBA_8888_SkColorType: - srcConfig = SkSwizzler::kRGBX; - break; - case kBGRA_8888_SkColorType: - srcConfig = SkSwizzler::kBGRX; + case kN32_SkColorType: + srcConfig = SkSwizzler::kNoOp32; break; case kRGB_565_SkColorType: - srcConfig = SkSwizzler::kRGB_565; + srcConfig = SkSwizzler::kNoOp16; break; default: // This function should only be called if the colorType is supported by jpeg @@ -348,7 +528,21 @@ void SkJpegCodec::initializeSwizzler(const SkImageInfo& dstInfo, const Options& } } - fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, options)); + if (JCS_RGB == fDecoderMgr->dinfo()->out_color_space) { + srcConfig = SkSwizzler::kRGB; + } + + Options swizzlerOptions = options; + if (options.fSubset) { + // Use fSwizzlerSubset if this is a subset decode. This is necessary in the case + // where libjpeg-turbo provides a subset and then we need to subset it further. + // Also, verify that fSwizzlerSubset is initialized and valid. + SkASSERT(!fSwizzlerSubset.isEmpty() && fSwizzlerSubset.x() <= options.fSubset->x() && + fSwizzlerSubset.width() == options.fSubset->width()); + swizzlerOptions.fSubset = &fSwizzlerSubset; + } + fSwizzler.reset(SkSwizzler::CreateSwizzler(srcConfig, nullptr, dstInfo, swizzlerOptions)); + SkASSERT(fSwizzler); fStorage.reset(get_row_bytes(fDecoderMgr->dinfo())); fSrcRow = fStorage.get(); } @@ -379,7 +573,7 @@ SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, // Remove objects used for sampling. fSwizzler.reset(nullptr); fSrcRow = nullptr; - fStorage.free(); + fStorage.reset(); // Now, given valid output dimensions, we can start the decompress if (!jpeg_start_decompress(fDecoderMgr->dinfo())) { @@ -387,33 +581,85 @@ SkCodec::Result SkJpegCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, return kInvalidInput; } - // We will need a swizzler if we are performing a subset decode or - // converting from CMYK. - if (options.fSubset || JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { + if (options.fSubset) { + fSwizzlerSubset = *options.fSubset; + } + +#ifdef TURBO_HAS_CROP + if (options.fSubset) { + uint32_t startX = options.fSubset->x(); + uint32_t width = options.fSubset->width(); + + // libjpeg-turbo may need to align startX to a multiple of the IDCT + // block size. If this is the case, it will decrease the value of + // startX to the appropriate alignment and also increase the value + // of width so that the right edge of the requested subset remains + // the same. + jpeg_crop_scanline(fDecoderMgr->dinfo(), &startX, &width); + + SkASSERT(startX <= (uint32_t) options.fSubset->x()); + SkASSERT(width >= (uint32_t) options.fSubset->width()); + SkASSERT(startX + width >= (uint32_t) options.fSubset->right()); + + // Instruct the swizzler (if it is necessary) to further subset the + // output provided by libjpeg-turbo. + // + // We set this here (rather than in the if statement below), so that + // if (1) we don't need a swizzler for the subset, and (2) we need a + // swizzler for CMYK, the swizzler will still use the proper subset + // dimensions. + // + // Note that the swizzler will ignore the y and height parameters of + // the subset. Since the scanline decoder (and the swizzler) handle + // one row at a time, only the subsetting in the x-dimension matters. + fSwizzlerSubset.setXYWH(options.fSubset->x() - startX, 0, + options.fSubset->width(), options.fSubset->height()); + + // We will need a swizzler if libjpeg-turbo cannot provide the exact + // subset that we request. + if (startX != (uint32_t) options.fSubset->x() || + width != (uint32_t) options.fSubset->width()) { + this->initializeSwizzler(dstInfo, options); + } + } + + // Make sure we have a swizzler if we are converting from CMYK. + if (!fSwizzler && JCS_CMYK == fDecoderMgr->dinfo()->out_color_space) { this->initializeSwizzler(dstInfo, options); } +#else + // We will need a swizzler if we are performing a subset decode or + // converting from CMYK. + J_COLOR_SPACE colorSpace = fDecoderMgr->dinfo()->out_color_space; + if (options.fSubset || JCS_CMYK == colorSpace || JCS_RGB == colorSpace) { + this->initializeSwizzler(dstInfo, options); + } +#endif return kSuccess; } -int SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { +int SkJpegCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { // Set the jump location for libjpeg errors if (setjmp(fDecoderMgr->getJmpBuf())) { return fDecoderMgr->returnFailure("setjmp", kInvalidInput); } // Read rows one at a time JSAMPLE* dstRow; + size_t srcRowBytes = get_row_bytes(fDecoderMgr->dinfo()); if (fSwizzler) { // write data to storage row, then sample using swizzler dstRow = fSrcRow; } else { // write data directly to dst + SkASSERT(count == 1 || dstRowBytes >= srcRowBytes); dstRow = (JSAMPLE*) dst; } for (int y = 0; y < count; y++) { // Read row of the image uint32_t rowsDecoded = jpeg_read_scanlines(fDecoderMgr->dinfo(), &dstRow, 1); + sk_msan_mark_initialized(dstRow, dstRow + srcRowBytes, "skbug.com/4550"); if (rowsDecoded != 1) { fDecoderMgr->dinfo()->output_scanline = this->dstInfo().height(); return y; @@ -422,33 +668,231 @@ int SkJpegCodec::onGetScanlines(void* dst, int count, size_t rowBytes) { if (fSwizzler) { // use swizzler to sample row fSwizzler->swizzle(dst, dstRow); - dst = SkTAddOffset(dst, rowBytes); + dst = SkTAddOffset(dst, dstRowBytes); } else { - dstRow = SkTAddOffset(dstRow, rowBytes); + dstRow = SkTAddOffset(dstRow, dstRowBytes); } } return count; } -#ifndef TURBO_HAS_SKIP -// TODO (msarett): Avoid reallocating the memory buffer on each call to skip. -static uint32_t jpeg_skip_scanlines(jpeg_decompress_struct* dinfo, int count) { - SkAutoTMalloc storage(get_row_bytes(dinfo)); - uint8_t* storagePtr = storage.get(); - for (int y = 0; y < count; y++) { - if (1 != jpeg_read_scanlines(dinfo, &storagePtr, 1)) { - return y; - } - } - return count; -} -#endif - bool SkJpegCodec::onSkipScanlines(int count) { // Set the jump location for libjpeg errors if (setjmp(fDecoderMgr->getJmpBuf())) { return fDecoderMgr->returnFalse("setjmp"); } +#ifdef TURBO_HAS_SKIP return (uint32_t) count == jpeg_skip_scanlines(fDecoderMgr->dinfo(), count); +#else + if (!fSrcRow) { + fStorage.reset(get_row_bytes(fDecoderMgr->dinfo())); + fSrcRow = fStorage.get(); + } + + for (int y = 0; y < count; y++) { + if (1 != jpeg_read_scanlines(fDecoderMgr->dinfo(), &fSrcRow, 1)) { + return false; + } + } + return true; +#endif +} + +static bool is_yuv_supported(jpeg_decompress_struct* dinfo) { + // Scaling is not supported in raw data mode. + SkASSERT(dinfo->scale_num == dinfo->scale_denom); + + // I can't imagine that this would ever change, but we do depend on it. + static_assert(8 == DCTSIZE, "DCTSIZE (defined in jpeg library) should always be 8."); + + if (JCS_YCbCr != dinfo->jpeg_color_space) { + return false; + } + + SkASSERT(3 == dinfo->num_components); + SkASSERT(dinfo->comp_info); + + // It is possible to perform a YUV decode for any combination of + // horizontal and vertical sampling that is supported by + // libjpeg/libjpeg-turbo. However, we will start by supporting only the + // common cases (where U and V have samp_factors of one). + // + // The definition of samp_factor is kind of the opposite of what SkCodec + // thinks of as a sampling factor. samp_factor is essentially a + // multiplier, and the larger the samp_factor is, the more samples that + // there will be. Ex: + // U_plane_width = image_width * (U_h_samp_factor / max_h_samp_factor) + // + // Supporting cases where the samp_factors for U or V were larger than + // that of Y would be an extremely difficult change, given that clients + // allocate memory as if the size of the Y plane is always the size of the + // image. However, this case is very, very rare. + if ((1 != dinfo->comp_info[1].h_samp_factor) || + (1 != dinfo->comp_info[1].v_samp_factor) || + (1 != dinfo->comp_info[2].h_samp_factor) || + (1 != dinfo->comp_info[2].v_samp_factor)) + { + return false; + } + + // Support all common cases of Y samp_factors. + // TODO (msarett): As mentioned above, it would be possible to support + // more combinations of samp_factors. The issues are: + // (1) Are there actually any images that are not covered + // by these cases? + // (2) How much complexity would be added to the + // implementation in order to support these rare + // cases? + int hSampY = dinfo->comp_info[0].h_samp_factor; + int vSampY = dinfo->comp_info[0].v_samp_factor; + return (1 == hSampY && 1 == vSampY) || + (2 == hSampY && 1 == vSampY) || + (2 == hSampY && 2 == vSampY) || + (1 == hSampY && 2 == vSampY) || + (4 == hSampY && 1 == vSampY) || + (4 == hSampY && 2 == vSampY); +} + +bool SkJpegCodec::onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const { + jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); + if (!is_yuv_supported(dinfo)) { + return false; + } + + sizeInfo->fSizes[SkYUVSizeInfo::kY].set(dinfo->comp_info[0].downsampled_width, + dinfo->comp_info[0].downsampled_height); + sizeInfo->fSizes[SkYUVSizeInfo::kU].set(dinfo->comp_info[1].downsampled_width, + dinfo->comp_info[1].downsampled_height); + sizeInfo->fSizes[SkYUVSizeInfo::kV].set(dinfo->comp_info[2].downsampled_width, + dinfo->comp_info[2].downsampled_height); + sizeInfo->fWidthBytes[SkYUVSizeInfo::kY] = dinfo->comp_info[0].width_in_blocks * DCTSIZE; + sizeInfo->fWidthBytes[SkYUVSizeInfo::kU] = dinfo->comp_info[1].width_in_blocks * DCTSIZE; + sizeInfo->fWidthBytes[SkYUVSizeInfo::kV] = dinfo->comp_info[2].width_in_blocks * DCTSIZE; + + if (colorSpace) { + *colorSpace = kJPEG_SkYUVColorSpace; + } + + return true; +} + +SkCodec::Result SkJpegCodec::onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) { + SkYUVSizeInfo defaultInfo; + + // This will check is_yuv_supported(), so we don't need to here. + bool supportsYUV = this->onQueryYUV8(&defaultInfo, nullptr); + if (!supportsYUV || + sizeInfo.fSizes[SkYUVSizeInfo::kY] != defaultInfo.fSizes[SkYUVSizeInfo::kY] || + sizeInfo.fSizes[SkYUVSizeInfo::kU] != defaultInfo.fSizes[SkYUVSizeInfo::kU] || + sizeInfo.fSizes[SkYUVSizeInfo::kV] != defaultInfo.fSizes[SkYUVSizeInfo::kV] || + sizeInfo.fWidthBytes[SkYUVSizeInfo::kY] < defaultInfo.fWidthBytes[SkYUVSizeInfo::kY] || + sizeInfo.fWidthBytes[SkYUVSizeInfo::kU] < defaultInfo.fWidthBytes[SkYUVSizeInfo::kU] || + sizeInfo.fWidthBytes[SkYUVSizeInfo::kV] < defaultInfo.fWidthBytes[SkYUVSizeInfo::kV]) { + return fDecoderMgr->returnFailure("onGetYUV8Planes", kInvalidInput); + } + + // Set the jump location for libjpeg errors + if (setjmp(fDecoderMgr->getJmpBuf())) { + return fDecoderMgr->returnFailure("setjmp", kInvalidInput); + } + + // Get a pointer to the decompress info since we will use it quite frequently + jpeg_decompress_struct* dinfo = fDecoderMgr->dinfo(); + + dinfo->raw_data_out = TRUE; + if (!jpeg_start_decompress(dinfo)) { + return fDecoderMgr->returnFailure("startDecompress", kInvalidInput); + } + + // A previous implementation claims that the return value of is_yuv_supported() + // may change after calling jpeg_start_decompress(). It looks to me like this + // was caused by a bug in the old code, but we'll be safe and check here. + SkASSERT(is_yuv_supported(dinfo)); + + // Currently, we require that the Y plane dimensions match the image dimensions + // and that the U and V planes are the same dimensions. + SkASSERT(sizeInfo.fSizes[SkYUVSizeInfo::kU] == sizeInfo.fSizes[SkYUVSizeInfo::kV]); + SkASSERT((uint32_t) sizeInfo.fSizes[SkYUVSizeInfo::kY].width() == dinfo->output_width && + (uint32_t) sizeInfo.fSizes[SkYUVSizeInfo::kY].height() == dinfo->output_height); + + // Build a JSAMPIMAGE to handle output from libjpeg-turbo. A JSAMPIMAGE has + // a 2-D array of pixels for each of the components (Y, U, V) in the image. + // Cheat Sheet: + // JSAMPIMAGE == JSAMPLEARRAY* == JSAMPROW** == JSAMPLE*** + JSAMPARRAY yuv[3]; + + // Set aside enough space for pointers to rows of Y, U, and V. + JSAMPROW rowptrs[2 * DCTSIZE + DCTSIZE + DCTSIZE]; + yuv[0] = &rowptrs[0]; // Y rows (DCTSIZE or 2 * DCTSIZE) + yuv[1] = &rowptrs[2 * DCTSIZE]; // U rows (DCTSIZE) + yuv[2] = &rowptrs[3 * DCTSIZE]; // V rows (DCTSIZE) + + // Initialize rowptrs. + int numYRowsPerBlock = DCTSIZE * dinfo->comp_info[0].v_samp_factor; + for (int i = 0; i < numYRowsPerBlock; i++) { + rowptrs[i] = SkTAddOffset(planes[SkYUVSizeInfo::kY], + i * sizeInfo.fWidthBytes[SkYUVSizeInfo::kY]); + } + for (int i = 0; i < DCTSIZE; i++) { + rowptrs[i + 2 * DCTSIZE] = SkTAddOffset(planes[SkYUVSizeInfo::kU], + i * sizeInfo.fWidthBytes[SkYUVSizeInfo::kU]); + rowptrs[i + 3 * DCTSIZE] = SkTAddOffset(planes[SkYUVSizeInfo::kV], + i * sizeInfo.fWidthBytes[SkYUVSizeInfo::kV]); + } + + // After each loop iteration, we will increment pointers to Y, U, and V. + size_t blockIncrementY = numYRowsPerBlock * sizeInfo.fWidthBytes[SkYUVSizeInfo::kY]; + size_t blockIncrementU = DCTSIZE * sizeInfo.fWidthBytes[SkYUVSizeInfo::kU]; + size_t blockIncrementV = DCTSIZE * sizeInfo.fWidthBytes[SkYUVSizeInfo::kV]; + + uint32_t numRowsPerBlock = numYRowsPerBlock; + + // We intentionally round down here, as this first loop will only handle + // full block rows. As a special case at the end, we will handle any + // remaining rows that do not make up a full block. + const int numIters = dinfo->output_height / numRowsPerBlock; + for (int i = 0; i < numIters; i++) { + JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock); + if (linesRead < numRowsPerBlock) { + // FIXME: Handle incomplete YUV decodes without signalling an error. + return kInvalidInput; + } + + // Update rowptrs. + for (int i = 0; i < numYRowsPerBlock; i++) { + rowptrs[i] += blockIncrementY; + } + for (int i = 0; i < DCTSIZE; i++) { + rowptrs[i + 2 * DCTSIZE] += blockIncrementU; + rowptrs[i + 3 * DCTSIZE] += blockIncrementV; + } + } + + uint32_t remainingRows = dinfo->output_height - dinfo->output_scanline; + SkASSERT(remainingRows == dinfo->output_height % numRowsPerBlock); + SkASSERT(dinfo->output_scanline == numIters * numRowsPerBlock); + if (remainingRows > 0) { + // libjpeg-turbo needs memory to be padded by the block sizes. We will fulfill + // this requirement using a dummy row buffer. + // FIXME: Should SkCodec have an extra memory buffer that can be shared among + // all of the implementations that use temporary/garbage memory? + SkAutoTMalloc dummyRow(sizeInfo.fWidthBytes[SkYUVSizeInfo::kY]); + for (int i = remainingRows; i < numYRowsPerBlock; i++) { + rowptrs[i] = dummyRow.get(); + } + int remainingUVRows = dinfo->comp_info[1].downsampled_height - DCTSIZE * numIters; + for (int i = remainingUVRows; i < DCTSIZE; i++) { + rowptrs[i + 2 * DCTSIZE] = dummyRow.get(); + rowptrs[i + 3 * DCTSIZE] = dummyRow.get(); + } + + JDIMENSION linesRead = jpeg_read_raw_data(dinfo, yuv, numRowsPerBlock); + if (linesRead < remainingRows) { + // FIXME: Handle incomplete YUV decodes without signalling an error. + return kInvalidInput; + } + } + + return kSuccess; } diff --git a/gfx/skia/skia/src/codec/SkJpegCodec.h b/gfx/skia/skia/src/codec/SkJpegCodec.h index 8e2db81b73..d3ea132da7 100644 --- a/gfx/skia/skia/src/codec/SkJpegCodec.h +++ b/gfx/skia/skia/src/codec/SkJpegCodec.h @@ -9,15 +9,13 @@ #define SkJpegCodec_DEFINED #include "SkCodec.h" +#include "SkColorSpace.h" #include "SkImageInfo.h" -#include "SkJpegDecoderMgr.h" -#include "SkJpegUtility_codec.h" +#include "SkSwizzler.h" #include "SkStream.h" #include "SkTemplates.h" -extern "C" { - #include "jpeglib.h" -} +class JpegDecoderMgr; /* * @@ -48,6 +46,10 @@ protected: Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&, SkPMColor*, int*, int*) override; + bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override; + + Result onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override; + SkEncodedFormat onGetEncodedFormat() const override { return kJPEG_SkEncodedFormat; } @@ -89,7 +91,8 @@ private: * @param decoderMgr holds decompress struct, src manager, and error manager * takes ownership */ - SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, JpegDecoderMgr* decoderMgr); + SkJpegCodec(const SkImageInfo& srcInfo, SkStream* stream, JpegDecoderMgr* decoderMgr, + sk_sp colorSpace, Origin origin); /* * Checks if the conversion between the input image and the requested output @@ -114,6 +117,10 @@ private: // scanline decoding SkAutoTMalloc fStorage; // Only used if sampling is needed uint8_t* fSrcRow; // Only used if sampling is needed + // libjpeg-turbo provides some subsetting. In the case that libjpeg-turbo + // cannot take the exact the subset that we need, we will use the swizzler + // to further subset the output from libjpeg-turbo. + SkIRect fSwizzlerSubset; SkAutoTDelete fSwizzler; typedef SkCodec INHERITED; diff --git a/gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp b/gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp index cc44f3c421..63228bb585 100644 --- a/gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp +++ b/gfx/skia/skia/src/codec/SkJpegDecoderMgr.cpp @@ -6,7 +6,8 @@ */ #include "SkJpegDecoderMgr.h" -#include "SkJpegUtility_codec.h" + +#include "SkJpegUtility.h" /* * Print information, warning, and error messages diff --git a/gfx/skia/skia/src/codec/SkJpegDecoderMgr.h b/gfx/skia/skia/src/codec/SkJpegDecoderMgr.h index 8e938ad535..e1127bad34 100644 --- a/gfx/skia/skia/src/codec/SkJpegDecoderMgr.h +++ b/gfx/skia/skia/src/codec/SkJpegDecoderMgr.h @@ -10,11 +10,8 @@ #include "SkCodec.h" #include "SkCodecPriv.h" -#include "SkJpegUtility_codec.h" -#include "SkSwizzler.h" - -// stdio is needed for jpeglib #include +#include "SkJpegUtility.h" extern "C" { #include "jpeglib.h" diff --git a/gfx/skia/skia/src/codec/SkJpegUtility_codec.cpp b/gfx/skia/skia/src/codec/SkJpegUtility.cpp similarity index 98% rename from gfx/skia/skia/src/codec/SkJpegUtility_codec.cpp rename to gfx/skia/skia/src/codec/SkJpegUtility.cpp index 19ece5e6ad..2cf36bacf1 100644 --- a/gfx/skia/skia/src/codec/SkJpegUtility_codec.cpp +++ b/gfx/skia/skia/src/codec/SkJpegUtility.cpp @@ -5,8 +5,9 @@ * found in the LICENSE file. */ +#include "SkJpegUtility.h" + #include "SkCodecPriv.h" -#include "SkJpegUtility_codec.h" /* * Initialize the source manager diff --git a/gfx/skia/skia/src/codec/SkJpegUtility_codec.h b/gfx/skia/skia/src/codec/SkJpegUtility.h similarity index 100% rename from gfx/skia/skia/src/codec/SkJpegUtility_codec.h rename to gfx/skia/skia/src/codec/SkJpegUtility.h diff --git a/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp b/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp index 01502cbd4a..7630a7b59f 100644 --- a/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp +++ b/gfx/skia/skia/src/codec/SkMaskSwizzler.cpp @@ -235,28 +235,23 @@ SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(const SkImageInfo& dstInfo, case 16: switch (dstInfo.colorType()) { case kN32_SkColorType: - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask16_to_n32_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask16_to_n32_premul; - break; - case kOpaque_SkAlphaType: - proc = &swizzle_mask16_to_n32_opaque; - break; - default: - break; + if (kOpaque_SkAlphaType == srcInfo.alphaType()) { + proc = &swizzle_mask16_to_n32_opaque; + } else { + switch (dstInfo.alphaType()) { + case kUnpremul_SkAlphaType: + proc = &swizzle_mask16_to_n32_unpremul; + break; + case kPremul_SkAlphaType: + proc = &swizzle_mask16_to_n32_premul; + break; + default: + break; + } } break; case kRGB_565_SkColorType: - switch (dstInfo.alphaType()) { - case kOpaque_SkAlphaType: - proc = &swizzle_mask16_to_565; - break; - default: - break; - } + proc = &swizzle_mask16_to_565; break; default: break; @@ -265,28 +260,23 @@ SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(const SkImageInfo& dstInfo, case 24: switch (dstInfo.colorType()) { case kN32_SkColorType: - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask24_to_n32_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask24_to_n32_premul; - break; - case kOpaque_SkAlphaType: - proc = &swizzle_mask24_to_n32_opaque; - break; - default: - break; + if (kOpaque_SkAlphaType == srcInfo.alphaType()) { + proc = &swizzle_mask24_to_n32_opaque; + } else { + switch (dstInfo.alphaType()) { + case kUnpremul_SkAlphaType: + proc = &swizzle_mask24_to_n32_unpremul; + break; + case kPremul_SkAlphaType: + proc = &swizzle_mask24_to_n32_premul; + break; + default: + break; + } } break; case kRGB_565_SkColorType: - switch (dstInfo.alphaType()) { - case kOpaque_SkAlphaType: - proc = &swizzle_mask24_to_565; - break; - default: - break; - } + proc = &swizzle_mask24_to_565; break; default: break; @@ -295,28 +285,23 @@ SkMaskSwizzler* SkMaskSwizzler::CreateMaskSwizzler(const SkImageInfo& dstInfo, case 32: switch (dstInfo.colorType()) { case kN32_SkColorType: - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: - proc = &swizzle_mask32_to_n32_unpremul; - break; - case kPremul_SkAlphaType: - proc = &swizzle_mask32_to_n32_premul; - break; - case kOpaque_SkAlphaType: - proc = &swizzle_mask32_to_n32_opaque; - break; - default: - break; + if (kOpaque_SkAlphaType == srcInfo.alphaType()) { + proc = &swizzle_mask32_to_n32_opaque; + } else { + switch (dstInfo.alphaType()) { + case kUnpremul_SkAlphaType: + proc = &swizzle_mask32_to_n32_unpremul; + break; + case kPremul_SkAlphaType: + proc = &swizzle_mask32_to_n32_premul; + break; + default: + break; + } } break; case kRGB_565_SkColorType: - switch (dstInfo.alphaType()) { - case kOpaque_SkAlphaType: - proc = &swizzle_mask32_to_565; - break; - default: - break; - } + proc = &swizzle_mask32_to_565; break; default: break; diff --git a/gfx/skia/skia/src/codec/SkMasks.cpp b/gfx/skia/skia/src/codec/SkMasks.cpp index 3126672f22..ac97a39d78 100644 --- a/gfx/skia/skia/src/codec/SkMasks.cpp +++ b/gfx/skia/skia/src/codec/SkMasks.cpp @@ -47,7 +47,7 @@ const static uint8_t n_bit_to_8_bit_lookup_table[] = { * Convert an n bit component to an 8-bit component * */ -static uint8_t convert_to_8(uint32_t component, uint32_t n) { +static uint8_t convert_to_8(uint8_t component, uint32_t n) { if (0 == n) { return 0; } else if (8 > n) { @@ -87,11 +87,6 @@ uint8_t SkMasks::getAlpha(uint32_t pixel) const { * */ const SkMasks::MaskInfo process_mask(uint32_t mask, uint32_t bpp) { - // Trim the masks to the allowed number of bits - if (bpp < 32) { - mask &= (1 << bpp) - 1; - } - // Determine properties of the mask uint32_t tempMask = mask; uint32_t shift = 0; @@ -105,14 +100,19 @@ const SkMasks::MaskInfo process_mask(uint32_t mask, uint32_t bpp) { for (; tempMask & 1; tempMask >>= 1) { size++; } - // Check that the mask is continuous - if (tempMask != 0) { - SkCodecPrintf("Warning: Bit masks is not continuous.\n"); + // Verify that the mask is continuous + if (tempMask) { + SkCodecPrintf("Warning: Bit mask is not continuous.\n"); + // Finish processing the mask + for (; tempMask; tempMask >>= 1) { + size++; + } } // Truncate masks greater than 8 bits if (size > 8) { shift += size - 8; size = 8; + mask &= 0xFF << shift; } } diff --git a/gfx/skia/skia/src/codec/SkCodec_libpng.cpp b/gfx/skia/skia/src/codec/SkPngCodec.cpp similarity index 62% rename from gfx/skia/skia/src/codec/SkCodec_libpng.cpp rename to gfx/skia/skia/src/codec/SkPngCodec.cpp index e8fd2a3cdd..8ae2360f25 100644 --- a/gfx/skia/skia/src/codec/SkCodec_libpng.cpp +++ b/gfx/skia/skia/src/codec/SkPngCodec.cpp @@ -5,41 +5,19 @@ * found in the LICENSE file. */ -#include "SkCodec_libpng.h" +#include "SkBitmap.h" #include "SkCodecPriv.h" #include "SkColorPriv.h" +#include "SkColorSpace.h" #include "SkColorTable.h" -#include "SkBitmap.h" #include "SkMath.h" +#include "SkOpts.h" +#include "SkPngCodec.h" #include "SkSize.h" #include "SkStream.h" #include "SkSwizzler.h" #include "SkTemplates.h" - -/////////////////////////////////////////////////////////////////////////////// -// Helper macros -/////////////////////////////////////////////////////////////////////////////// - -#ifndef png_jmpbuf -# define png_jmpbuf(png_ptr) ((png_ptr)->jmpbuf) -#endif - -/* These were dropped in libpng >= 1.4 */ -#ifndef png_infopp_NULL - #define png_infopp_NULL nullptr -#endif - -#ifndef png_bytepp_NULL - #define png_bytepp_NULL nullptr -#endif - -#ifndef int_p_NULL - #define int_p_NULL nullptr -#endif - -#ifndef png_flush_ptr_NULL - #define png_flush_ptr_NULL nullptr -#endif +#include "SkUtils.h" /////////////////////////////////////////////////////////////////////////////// // Callback functions @@ -88,7 +66,7 @@ public: // fInfo_ptr will never be non-nullptr unless fPng_ptr is. if (fPng_ptr) { png_infopp info_pp = fInfo_ptr ? &fInfo_ptr : nullptr; - png_destroy_read_struct(&fPng_ptr, info_pp, png_infopp_NULL); + png_destroy_read_struct(&fPng_ptr, info_pp, nullptr); } } @@ -97,7 +75,7 @@ public: fInfo_ptr = info_ptr; } - void detach() { + void release() { fPng_ptr = nullptr; fInfo_ptr = nullptr; } @@ -108,91 +86,75 @@ private: }; #define AutoCleanPng(...) SK_REQUIRE_LOCAL_VAR(AutoCleanPng) -//checks if there is transparency info in the tRNS chunk -//image types which could have data in the tRNS chunk include: Index8, Gray8, RGB -static bool has_transparency_in_tRNS(png_structp png_ptr, - png_infop info_ptr) { - if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { - return false; - } - - png_bytep trans; - int num_trans; - png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, nullptr); - return num_trans > 0; -} - // Method for coverting to either an SkPMColor or a similarly packed // unpremultiplied color. typedef uint32_t (*PackColorProc)(U8CPU a, U8CPU r, U8CPU g, U8CPU b); // Note: SkColorTable claims to store SkPMColors, which is not necessarily // the case here. +// TODO: If we add support for non-native swizzles, we'll need to handle that here. bool SkPngCodec::decodePalette(bool premultiply, int* ctableCount) { - int numPalette; - png_colorp palette; - png_bytep trans; - if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numPalette)) { + int numColors; + png_color* palette; + if (!png_get_PLTE(fPng_ptr, fInfo_ptr, &palette, &numColors)) { return false; } - // Note: These are not necessarily SkPMColors - SkPMColor colorStorage[256]; // worst-case storage - SkPMColor* colorPtr = colorStorage; + // Note: These are not necessarily SkPMColors. + SkPMColor colorPtr[256]; - int numTrans; - if (png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)) { - png_get_tRNS(fPng_ptr, fInfo_ptr, &trans, &numTrans, nullptr); - } else { - numTrans = 0; + png_bytep alphas; + int numColorsWithAlpha = 0; + if (png_get_tRNS(fPng_ptr, fInfo_ptr, &alphas, &numColorsWithAlpha, nullptr)) { + // Choose which function to use to create the color table. If the final destination's + // colortype is unpremultiplied, the color table will store unpremultiplied colors. + PackColorProc proc; + if (premultiply) { + proc = &SkPremultiplyARGBInline; + } else { + proc = &SkPackARGB32NoCheck; + } + + for (int i = 0; i < numColorsWithAlpha; i++) { + // We don't have a function in SkOpts that combines a set of alphas with a set + // of RGBs. We could write one, but it's hardly worth it, given that this + // is such a small fraction of the total decode time. + colorPtr[i] = proc(alphas[i], palette->red, palette->green, palette->blue); + palette++; + } } - // check for bad images that might make us crash - if (numTrans > numPalette) { - numTrans = numPalette; + if (numColorsWithAlpha < numColors) { + // The optimized code depends on a 3-byte png_color struct with the colors + // in RGB order. These checks make sure it is safe to use. + static_assert(3 == sizeof(png_color), "png_color struct has changed. Opts are broken."); +#ifdef SK_DEBUG + SkASSERT(&palette->red < &palette->green); + SkASSERT(&palette->green < &palette->blue); +#endif + +#ifdef SK_PMCOLOR_IS_RGBA + SkOpts::RGB_to_RGB1(colorPtr + numColorsWithAlpha, palette, numColors - numColorsWithAlpha); +#else + SkOpts::RGB_to_BGR1(colorPtr + numColorsWithAlpha, palette, numColors - numColorsWithAlpha); +#endif } - int index = 0; - - // Choose which function to use to create the color table. If the final destination's - // colortype is unpremultiplied, the color table will store unpremultiplied colors. - PackColorProc proc; - if (premultiply) { - proc = &SkPreMultiplyARGB; - } else { - proc = &SkPackARGB32NoCheck; - } - for (; index < numTrans; index++) { - *colorPtr++ = proc(*trans++, palette->red, palette->green, palette->blue); - palette++; + // Pad the color table with the last color in the table (or black) in the case that + // invalid pixel indices exceed the number of colors in the table. + const int maxColors = 1 << fBitDepth; + if (numColors < maxColors) { + SkPMColor lastColor = numColors > 0 ? colorPtr[numColors - 1] : SK_ColorBLACK; + sk_memset32(colorPtr + numColors, lastColor, maxColors - numColors); } - for (; index < numPalette; index++) { - *colorPtr++ = SkPackARGB32(0xFF, palette->red, palette->green, palette->blue); - palette++; - } - - /* BUGGY IMAGE WORKAROUND - Invalid images could contain pixel values that are greater than the number of palette - entries. Since we use pixel values as indices into the palette this could result in reading - beyond the end of the palette which could leak the contents of uninitialized memory. To - ensure this doesn't happen, we grow the colortable to the maximum size that can be - addressed by the bitdepth of the image and fill it with the last palette color or black if - the palette is empty (really broken image). - */ - int colorCount = SkTMax(numPalette, 1 << SkTMin(fBitDepth, 8)); - SkPMColor lastColor = index > 0 ? colorPtr[-1] : SkPackARGB32(0xFF, 0, 0, 0); - for (; index < colorCount; index++) { - *colorPtr++ = lastColor; - } - - // Set the new color count + // Set the new color count. if (ctableCount != nullptr) { - *ctableCount = colorCount; + *ctableCount = maxColors; } - fColorTable.reset(new SkColorTable(colorStorage, colorCount)); + fColorTable.reset(new SkColorTable(colorPtr, maxColors)); return true; } @@ -204,6 +166,108 @@ bool SkPngCodec::IsPng(const char* buf, size_t bytesRead) { return !png_sig_cmp((png_bytep) buf, (png_size_t)0, bytesRead); } +static float png_fixed_point_to_float(png_fixed_point x) { + // We multiply by the same factor that libpng used to convert + // fixed point -> double. Since we want floats, we choose to + // do the conversion ourselves rather than convert + // fixed point -> double -> float. + return ((float) x) * 0.00001f; +} + +static float png_inverted_fixed_point_to_float(png_fixed_point x) { + // This is necessary because the gAMA chunk actually stores 1/gamma. + return 1.0f / png_fixed_point_to_float(x); +} + +// Returns a colorSpace object that represents any color space information in +// the encoded data. If the encoded data contains no color space, this will +// return NULL. +sk_sp read_color_space(png_structp png_ptr, png_infop info_ptr) { + +#if (PNG_LIBPNG_VER_MAJOR > 1) || (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 6) + + // First check for an ICC profile + png_bytep profile; + png_uint_32 length; + // The below variables are unused, however, we need to pass them in anyway or + // png_get_iCCP() will return nothing. + // Could knowing the |name| of the profile ever be interesting? Maybe for debugging? + png_charp name; + // The |compression| is uninteresting since: + // (1) libpng has already decompressed the profile for us. + // (2) "deflate" is the only mode of decompression that libpng supports. + int compression; + if (PNG_INFO_iCCP == png_get_iCCP(png_ptr, info_ptr, &name, &compression, &profile, + &length)) { + return SkColorSpace::NewICC(profile, length); + } + + // Second, check for sRGB. + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { + + // sRGB chunks also store a rendering intent: Absolute, Relative, + // Perceptual, and Saturation. + // FIXME (msarett): Extract this information from the sRGB chunk once + // we are able to handle this information in + // SkColorSpace. + return SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named); + } + + // Next, check for chromaticities. + png_fixed_point XYZ[9]; + SkFloat3x3 toXYZD50; + png_fixed_point gamma; + SkFloat3 gammas; + if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &XYZ[0], &XYZ[1], &XYZ[2], &XYZ[3], &XYZ[4], + &XYZ[5], &XYZ[6], &XYZ[7], &XYZ[8])) { + + // FIXME (msarett): Here we are treating XYZ values as D50 even though the color + // temperature is unspecified. I suspect that this assumption + // is most often ok, but we could also calculate the color + // temperature (D value) and then convert the XYZ to D50. Maybe + // we should add a new constructor to SkColorSpace that accepts + // XYZ with D-Unkown? + for (int i = 0; i < 9; i++) { + toXYZD50.fMat[i] = png_fixed_point_to_float(XYZ[i]); + } + + if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { + gammas.fVec[0] = gammas.fVec[1] = gammas.fVec[2] = + png_inverted_fixed_point_to_float(gamma); + } else { + // If the image does not specify gamma, let's choose linear. Should we default + // to sRGB? Most images are intended to be sRGB (gamma = 2.2f). + gammas.fVec[0] = gammas.fVec[1] = gammas.fVec[2] = 1.0f; + } + + + return SkColorSpace::NewRGB(toXYZD50, gammas); + } + + // Last, check for gamma. + if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) { + + // Guess a default value for cHRM? Or should we just give up? + // Here we use the identity matrix as a default. + // FIXME (msarett): Should SkFloat3x3 have a method to set the identity matrix? + memset(toXYZD50.fMat, 0, 9 * sizeof(float)); + toXYZD50.fMat[0] = toXYZD50.fMat[4] = toXYZD50.fMat[8] = 1.0f; + + // Set the gammas. + gammas.fVec[0] = gammas.fVec[1] = gammas.fVec[2] = png_inverted_fixed_point_to_float(gamma); + + return SkColorSpace::NewRGB(toXYZD50, gammas); + } + +#endif // LIBPNG >= 1.6 + + // Finally, what should we do if there is no color space information in the PNG? + // The specification says that this indicates "gamma is unknown" and that the + // "color is device dependent". I'm assuming we can represent this with NULL. + // But should we guess sRGB? Most images are sRGB, even if they don't specify. + return nullptr; +} + // Reads the header and initializes the output fields, if not NULL. // // @param stream Input data. Will be read to get enough information to properly @@ -265,86 +329,82 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, // PNG file before the first IDAT (image data chunk). png_read_info(png_ptr, info_ptr); png_uint_32 origWidth, origHeight; - int bitDepth, colorType; + int bitDepth, encodedColorType; png_get_IHDR(png_ptr, info_ptr, &origWidth, &origHeight, &bitDepth, - &colorType, int_p_NULL, int_p_NULL, int_p_NULL); + &encodedColorType, nullptr, nullptr, nullptr); if (bitDepthPtr) { *bitDepthPtr = bitDepth; } - // sanity check for size - { - int64_t size = sk_64_mul(origWidth, origHeight); - // now check that if we are 4-bytes per pixel, we also don't overflow - if (size < 0 || size > (0x7FFFFFFF >> 2)) { - return false; - } - } - - // Tell libpng to strip 16 bit/color files down to 8 bits/color + // Tell libpng to strip 16 bit/color files down to 8 bits/color. + // TODO: Should we handle this in SkSwizzler? Could this also benefit + // RAW decodes? if (bitDepth == 16) { + SkASSERT(PNG_COLOR_TYPE_PALETTE != encodedColorType); png_set_strip_16(png_ptr); } -#ifdef PNG_READ_PACK_SUPPORTED - // Extract multiple pixels with bit depths of 1, 2, and 4 from a single - // byte into separate bytes (useful for paletted and grayscale images). - if (bitDepth < 8) { - png_set_packing(png_ptr); - } -#endif - // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. - if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) { - png_set_expand_gray_1_2_4_to_8(png_ptr); - } - // Now determine the default SkColorType and SkAlphaType and set required transforms - SkColorType skColorType = kUnknown_SkColorType; - SkAlphaType skAlphaType = kUnknown_SkAlphaType; - switch (colorType) { + // Now determine the default colorType and alphaType and set the required transforms. + // Often, we depend on SkSwizzler to perform any transforms that we need. However, we + // still depend on libpng for many of the rare and PNG-specific cases. + SkColorType colorType = kUnknown_SkColorType; + SkAlphaType alphaType = kUnknown_SkAlphaType; + switch (encodedColorType) { case PNG_COLOR_TYPE_PALETTE: - skColorType = kIndex_8_SkColorType; - skAlphaType = has_transparency_in_tRNS(png_ptr, info_ptr) ? + // Extract multiple pixels with bit depths of 1, 2, and 4 from a single + // byte into separate bytes (useful for paletted and grayscale images). + if (bitDepth < 8) { + // TODO: Should we use SkSwizzler here? + png_set_packing(png_ptr); + } + + colorType = kIndex_8_SkColorType; + // Set the alpha type depending on if a transparency chunk exists. + alphaType = png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) ? kUnpremul_SkAlphaType : kOpaque_SkAlphaType; break; case PNG_COLOR_TYPE_RGB: - if (has_transparency_in_tRNS(png_ptr, info_ptr)) { - //convert to RGBA with tranparency information in tRNS chunk if it exists + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + // Convert to RGBA if transparency chunk exists. png_set_tRNS_to_alpha(png_ptr); - skAlphaType = kUnpremul_SkAlphaType; + alphaType = kUnpremul_SkAlphaType; } else { - //convert to RGBA with Opaque Alpha - png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); - skAlphaType = kOpaque_SkAlphaType; + alphaType = kOpaque_SkAlphaType; } - skColorType = kN32_SkColorType; + colorType = kN32_SkColorType; break; case PNG_COLOR_TYPE_GRAY: - if (has_transparency_in_tRNS(png_ptr, info_ptr)) { - //FIXME: support gray with alpha as a color type - //convert to RGBA if there is transparentcy info in the tRNS chunk + // Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel. + if (bitDepth < 8) { + // TODO: Should we use SkSwizzler here? + png_set_expand_gray_1_2_4_to_8(png_ptr); + } + + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); - png_set_gray_to_rgb(png_ptr); - skColorType = kN32_SkColorType; - skAlphaType = kUnpremul_SkAlphaType; + + // We will recommend kN32 here since we do not support kGray + // with alpha. + colorType = kN32_SkColorType; + alphaType = kUnpremul_SkAlphaType; } else { - skColorType = kGray_8_SkColorType; - skAlphaType = kOpaque_SkAlphaType; + colorType = kGray_8_SkColorType; + alphaType = kOpaque_SkAlphaType; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: - //FIXME: support gray with alpha as a color type - //convert to RGBA - png_set_gray_to_rgb(png_ptr); - skColorType = kN32_SkColorType; - skAlphaType = kUnpremul_SkAlphaType; + // We will recommend kN32 here since we do not support anything + // similar to GRAY_ALPHA. + colorType = kN32_SkColorType; + alphaType = kUnpremul_SkAlphaType; break; case PNG_COLOR_TYPE_RGBA: - skColorType = kN32_SkColorType; - skAlphaType = kUnpremul_SkAlphaType; + colorType = kN32_SkColorType; + alphaType = kUnpremul_SkAlphaType; break; default: - //all the color types have been covered above + // All the color types have been covered above. SkASSERT(false); } @@ -353,12 +413,15 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, *numberPassesPtr = numberPasses; } - // FIXME: Also need to check for sRGB ( https://bug.skia.org/3471 ). + SkColorProfileType profileType = kLinear_SkColorProfileType; + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sRGB)) { + profileType = kSRGB_SkColorProfileType; + } if (imageInfo) { - *imageInfo = SkImageInfo::Make(origWidth, origHeight, skColorType, skAlphaType); + *imageInfo = SkImageInfo::Make(origWidth, origHeight, colorType, alphaType, profileType); } - autoClean.detach(); + autoClean.release(); if (png_ptrp) { *png_ptrp = png_ptr; } @@ -370,8 +433,9 @@ static bool read_header(SkStream* stream, SkPngChunkReader* chunkReader, } SkPngCodec::SkPngCodec(const SkImageInfo& info, SkStream* stream, SkPngChunkReader* chunkReader, - png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses) - : INHERITED(info, stream) + png_structp png_ptr, png_infop info_ptr, int bitDepth, int numberPasses, + sk_sp colorSpace) + : INHERITED(info, stream, colorSpace) , fPngChunkReader(SkSafeRef(chunkReader)) , fPng_ptr(png_ptr) , fInfo_ptr(info_ptr) @@ -388,7 +452,7 @@ void SkPngCodec::destroyReadStruct() { if (fPng_ptr) { // We will never have a nullptr fInfo_ptr with a non-nullptr fPng_ptr SkASSERT(fInfo_ptr); - png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, png_infopp_NULL); + png_destroy_read_struct(&fPng_ptr, &fInfo_ptr, nullptr); fPng_ptr = nullptr; fInfo_ptr = nullptr; } @@ -408,12 +472,12 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, SkCodecPrintf("setjmp long jump!\n"); return kInvalidInput; } - png_read_update_info(fPng_ptr, fInfo_ptr); + png_read_update_info(fPng_ptr, fInfo_ptr); - //srcColorType was determined in read_header() which determined png color type - const SkColorType srcColorType = this->getInfo().colorType(); + // suggestedColorType was determined in read_header() based on the encodedColorType + const SkColorType suggestedColorType = this->getInfo().colorType(); - switch (srcColorType) { + switch (suggestedColorType) { case kIndex_8_SkColorType: //decode palette to Skia format fSrcConfig = SkSwizzler::kIndex; @@ -424,18 +488,32 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, break; case kGray_8_SkColorType: fSrcConfig = SkSwizzler::kGray; - break; - case kN32_SkColorType: - if (this->getInfo().alphaType() == kOpaque_SkAlphaType) { - fSrcConfig = SkSwizzler::kRGBX; + break; + case kN32_SkColorType: { + const uint8_t encodedColorType = png_get_color_type(fPng_ptr, fInfo_ptr); + if (PNG_COLOR_TYPE_GRAY_ALPHA == encodedColorType || + PNG_COLOR_TYPE_GRAY == encodedColorType) { + // If encodedColorType is GRAY, there must be a transparent chunk. + // Otherwise, suggestedColorType would be kGray. We have already + // instructed libpng to convert the transparent chunk to alpha, + // so we can treat both GRAY and GRAY_ALPHA as kGrayAlpha. + SkASSERT(encodedColorType == PNG_COLOR_TYPE_GRAY_ALPHA || + png_get_valid(fPng_ptr, fInfo_ptr, PNG_INFO_tRNS)); + + fSrcConfig = SkSwizzler::kGrayAlpha; + } else { + if (this->getInfo().alphaType() == kOpaque_SkAlphaType) { + fSrcConfig = SkSwizzler::kRGB; } else { fSrcConfig = SkSwizzler::kRGBA; + } } break; + } default: - //would have exited before now if the colorType was supported by png + // We will always recommend one of the above colorTypes. SkASSERT(false); - } + } // Copy the color table to the client if they request kIndex8 mode copy_color_table(requestedInfo, fColorTable, ctable, ctableCount); @@ -443,10 +521,8 @@ SkCodec::Result SkPngCodec::initializeSwizzler(const SkImageInfo& requestedInfo, // Create the swizzler. SkPngCodec retains ownership of the color table. const SkPMColor* colors = get_color_ptr(fColorTable.get()); fSwizzler.reset(SkSwizzler::CreateSwizzler(fSrcConfig, colors, requestedInfo, options)); - if (!fSwizzler) { - // FIXME: CreateSwizzler could fail for another reason. - return kUnimplemented; - } + SkASSERT(fSwizzler); + return kSuccess; } @@ -488,6 +564,12 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* if (result != kSuccess) { return result; } + + const int width = requestedInfo.width(); + const int height = requestedInfo.height(); + const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); + const size_t srcRowBytes = width * bpp; + // FIXME: Could we use the return value of setjmp to specify the type of // error? int row = 0; @@ -497,7 +579,7 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* // Assume that any error that occurs while reading rows is caused by an incomplete input. if (fNumberPasses > 1) { // FIXME (msarett): Handle incomplete interlaced pngs. - return kInvalidInput; + return (row == height) ? kSuccess : kInvalidInput; } // FIXME: We do a poor job on incomplete pngs compared to other decoders (ex: Chromium, // Ubuntu Image Viewer). This is because we use the default buffer size in libpng (8192 @@ -509,78 +591,63 @@ SkCodec::Result SkPngCodec::onGetPixels(const SkImageInfo& requestedInfo, void* // made a partial read. // Making our buffer size smaller improves our incomplete decodes, but what impact does // it have on regular decode performance? Should we investigate using a different API - // instead of png_read_row(s)? Chromium uses png_process_data. + // instead of png_read_row? Chromium uses png_process_data. *rowsDecoded = row; - return kIncompleteInput; + return (row == height) ? kSuccess : kIncompleteInput; } // FIXME: We could split these out based on subclass. void* dstRow = dst; if (fNumberPasses > 1) { - const int width = requestedInfo.width(); - const int height = requestedInfo.height(); - const int bpp = SkSwizzler::BytesPerPixel(fSrcConfig); - const size_t srcRowBytes = width * bpp; - - storage.reset(width * height * bpp); + storage.reset(height * srcRowBytes); uint8_t* const base = storage.get(); for (int i = 0; i < fNumberPasses; i++) { uint8_t* srcRow = base; for (int y = 0; y < height; y++) { - uint8_t* bmRow = srcRow; - png_read_rows(fPng_ptr, &bmRow, png_bytepp_NULL, 1); + png_read_row(fPng_ptr, srcRow, nullptr); srcRow += srcRowBytes; } } // Now swizzle it. uint8_t* srcRow = base; - for (int y = 0; y < height; y++) { + for (; row < height; row++) { fSwizzler->swizzle(dstRow, srcRow); dstRow = SkTAddOffset(dstRow, dstRowBytes); srcRow += srcRowBytes; } } else { - storage.reset(requestedInfo.width() * SkSwizzler::BytesPerPixel(fSrcConfig)); + storage.reset(srcRowBytes); uint8_t* srcRow = storage.get(); - for (; row < requestedInfo.height(); row++) { - png_read_rows(fPng_ptr, &srcRow, png_bytepp_NULL, 1); - // FIXME: Only call IsOpaque once, outside the loop. Same for onGetScanlines. + for (; row < height; row++) { + png_read_row(fPng_ptr, srcRow, nullptr); fSwizzler->swizzle(dstRow, srcRow); dstRow = SkTAddOffset(dstRow, dstRowBytes); } } - // FIXME: do we need substituteTranspColor? Note that we cannot do it for - // scanline decoding, but we could do it here. Alternatively, we could do - // it as we go, instead of in post-processing like SkPNGImageDecoder. - - if (setjmp(png_jmpbuf(fPng_ptr))) { - // We've already read all the scanlines. This is a success. - return kSuccess; - } - // read rest of file, and get additional comment and time chunks in info_ptr png_read_end(fPng_ptr, fInfo_ptr); return kSuccess; } -uint32_t SkPngCodec::onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const { +uint32_t SkPngCodec::onGetFillValue(SkColorType colorType) const { const SkPMColor* colorPtr = get_color_ptr(fColorTable.get()); if (colorPtr) { return get_color_table_fill_value(colorType, colorPtr, 0); } - return INHERITED::onGetFillValue(colorType, alphaType); + return INHERITED::onGetFillValue(colorType); } // Subclass of SkPngCodec which supports scanline decoding class SkPngScanlineDecoder : public SkPngCodec { public: SkPngScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, - SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr, int bitDepth) - : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1) + SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr, int bitDepth, + sk_sp colorSpace) + : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, 1, colorSpace) , fSrcRow(nullptr) {} @@ -612,7 +679,7 @@ public: void* dstRow = dst; for (; row < count; row++) { - png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); + png_read_row(this->png_ptr(), fSrcRow, nullptr); this->swizzler()->swizzle(dstRow, fSrcRow); dstRow = SkTAddOffset(dstRow, rowBytes); } @@ -626,11 +693,9 @@ public: SkCodecPrintf("setjmp long jump!\n"); return false; } - //there is a potential tradeoff of memory vs speed created by putting this in a loop. - //calling png_read_rows in a loop is insignificantly slower than calling it once with count - //as png_read_rows has it's own loop which calls png_read_row count times. + for (int row = 0; row < count; row++) { - png_read_rows(this->png_ptr(), &fSrcRow, png_bytepp_NULL, 1); + png_read_row(this->png_ptr(), fSrcRow, nullptr); } return true; } @@ -647,8 +712,9 @@ class SkPngInterlacedScanlineDecoder : public SkPngCodec { public: SkPngInterlacedScanlineDecoder(const SkImageInfo& srcInfo, SkStream* stream, SkPngChunkReader* chunkReader, png_structp png_ptr, png_infop info_ptr, - int bitDepth, int numberPasses) - : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, numberPasses) + int bitDepth, int numberPasses, sk_sp colorSpace) + : INHERITED(srcInfo, stream, chunkReader, png_ptr, info_ptr, bitDepth, numberPasses, + colorSpace) , fHeight(-1) , fCanSkipRewind(false) { @@ -658,7 +724,7 @@ public: Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, SkPMColor ctable[], int* ctableCount) override { if (!conversion_possible(dstInfo, this->getInfo())) { - return kInvalidConversion; + return kInvalidConversion; } const Result result = this->initializeSwizzler(dstInfo, options, ctable, @@ -714,17 +780,17 @@ public: for (int i = 0; i < this->numberPasses(); i++) { // read rows we planned to skip into garbage row for (int y = 0; y < startRow; y++){ - png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1); + png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); } // read rows we care about into buffer srcRow = storagePtr; for (int y = 0; y < count; y++) { - png_read_rows(this->png_ptr(), &srcRow, png_bytepp_NULL, 1); + png_read_row(this->png_ptr(), srcRow, nullptr); srcRow += fSrcRowBytes; } // read rows we don't want into garbage buffer for (int y = 0; y < fHeight - startRow - count; y++) { - png_read_rows(this->png_ptr(), &fGarbageRowPtr, png_bytepp_NULL, 1); + png_read_row(this->png_ptr(), fGarbageRowPtr, nullptr); } } //swizzle the rows we care about @@ -779,11 +845,14 @@ SkCodec* SkPngCodec::NewFromStream(SkStream* stream, SkPngChunkReader* chunkRead return nullptr; } + auto colorSpace = read_color_space(png_ptr, info_ptr); + if (1 == numberPasses) { - return new SkPngScanlineDecoder(imageInfo, streamDeleter.detach(), chunkReader, - png_ptr, info_ptr, bitDepth); + return new SkPngScanlineDecoder(imageInfo, streamDeleter.release(), chunkReader, + png_ptr, info_ptr, bitDepth, colorSpace); } - return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.detach(), chunkReader, - png_ptr, info_ptr, bitDepth, numberPasses); + return new SkPngInterlacedScanlineDecoder(imageInfo, streamDeleter.release(), chunkReader, + png_ptr, info_ptr, bitDepth, numberPasses, + colorSpace); } diff --git a/gfx/skia/skia/src/codec/SkCodec_libpng.h b/gfx/skia/skia/src/codec/SkPngCodec.h similarity index 94% rename from gfx/skia/skia/src/codec/SkCodec_libpng.h rename to gfx/skia/skia/src/codec/SkPngCodec.h index 9a13a12670..5673e5b4fa 100644 --- a/gfx/skia/skia/src/codec/SkCodec_libpng.h +++ b/gfx/skia/skia/src/codec/SkPngCodec.h @@ -31,7 +31,7 @@ protected: override; SkEncodedFormat onGetEncodedFormat() const override { return kPNG_SkEncodedFormat; } bool onRewind() override; - uint32_t onGetFillValue(SkColorType colorType, SkAlphaType alphaType) const override; + uint32_t onGetFillValue(SkColorType) const override; // Helper to set up swizzler and color table. Also calls png_read_update_info. Result initializeSwizzler(const SkImageInfo& requestedInfo, const Options&, @@ -41,7 +41,8 @@ protected: return fSwizzler; } - SkPngCodec(const SkImageInfo&, SkStream*, SkPngChunkReader*, png_structp, png_infop, int, int); + SkPngCodec(const SkImageInfo&, SkStream*, SkPngChunkReader*, png_structp, png_infop, int, int, + sk_sp); png_structp png_ptr() { return fPng_ptr; } SkSwizzler* swizzler() { return fSwizzler; } diff --git a/gfx/skia/skia/src/codec/SkRawAdapterCodec.cpp b/gfx/skia/skia/src/codec/SkRawAdapterCodec.cpp new file mode 100644 index 0000000000..76cbaa1a23 --- /dev/null +++ b/gfx/skia/skia/src/codec/SkRawAdapterCodec.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCodec.h" +#include "SkCodecPriv.h" +#include "SkRawAdapterCodec.h" + +SkRawAdapterCodec::SkRawAdapterCodec(SkRawCodec* codec) + : INHERITED(codec) +{} + +SkISize SkRawAdapterCodec::onGetSampledDimensions(int sampleSize) const { + float scale = 1.f / static_cast(sampleSize); + return this->codec()->getScaledDimensions(scale); +} + +SkCodec::Result SkRawAdapterCodec::onGetAndroidPixels( + const SkImageInfo& info, void* pixels, size_t rowBytes, + const AndroidOptions& options) { + SkCodec::Options codecOptions; + codecOptions.fZeroInitialized = options.fZeroInitialized; + codecOptions.fSubset = options.fSubset; + return this->codec()->getPixels( + info, pixels, rowBytes, &codecOptions, options.fColorPtr, + options.fColorCount); +} diff --git a/gfx/skia/skia/src/codec/SkRawAdapterCodec.h b/gfx/skia/skia/src/codec/SkRawAdapterCodec.h new file mode 100644 index 0000000000..b552f2aaea --- /dev/null +++ b/gfx/skia/skia/src/codec/SkRawAdapterCodec.h @@ -0,0 +1,42 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRawAdapterCodec_DEFINED +#define SkRawAdapterCodec_DEFINED + +#include "SkAndroidCodec.h" +#include "SkCodec.h" +#include "SkEncodedFormat.h" +#include "SkRawCodec.h" +#include "SkStream.h" +#include "SkTypes.h" + +/** + * This class implements the functionality of SkAndroidCodec. It uses an + * SkRawCodec. + */ +class SkRawAdapterCodec : public SkAndroidCodec { +public: + + explicit SkRawAdapterCodec(SkRawCodec*); + + virtual ~SkRawAdapterCodec() {} + +protected: + + SkISize onGetSampledDimensions(int sampleSize) const override; + + bool onGetSupportedSubset(SkIRect* /*desiredSubset*/) const override { return false; } + + SkCodec::Result onGetAndroidPixels(const SkImageInfo& info, void* pixels, size_t rowBytes, + const AndroidOptions& options) override; + +private: + + typedef SkAndroidCodec INHERITED; +}; +#endif // SkRawAdapterCodec_DEFINED diff --git a/gfx/skia/skia/src/codec/SkRawCodec.cpp b/gfx/skia/skia/src/codec/SkRawCodec.cpp new file mode 100644 index 0000000000..762e82364c --- /dev/null +++ b/gfx/skia/skia/src/codec/SkRawCodec.cpp @@ -0,0 +1,764 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCodec.h" +#include "SkCodecPriv.h" +#include "SkColorPriv.h" +#include "SkData.h" +#include "SkJpegCodec.h" +#include "SkMutex.h" +#include "SkRawCodec.h" +#include "SkRefCnt.h" +#include "SkStream.h" +#include "SkStreamPriv.h" +#include "SkSwizzler.h" +#include "SkTArray.h" +#include "SkTaskGroup.h" +#include "SkTemplates.h" +#include "SkTypes.h" + +#include "dng_area_task.h" +#include "dng_color_space.h" +#include "dng_errors.h" +#include "dng_exceptions.h" +#include "dng_host.h" +#include "dng_info.h" +#include "dng_memory.h" +#include "dng_render.h" +#include "dng_stream.h" + +#include "src/piex.h" + +#include // for std::round,floor,ceil +#include + +namespace { + +// Caluclates the number of tiles of tile_size that fit into the area in vertical and horizontal +// directions. +dng_point num_tiles_in_area(const dng_point &areaSize, + const dng_point_real64 &tileSize) { + // FIXME: Add a ceil_div() helper in SkCodecPriv.h + return dng_point(static_cast((areaSize.v + tileSize.v - 1) / tileSize.v), + static_cast((areaSize.h + tileSize.h - 1) / tileSize.h)); +} + +int num_tasks_required(const dng_point& tilesInTask, + const dng_point& tilesInArea) { + return ((tilesInArea.v + tilesInTask.v - 1) / tilesInTask.v) * + ((tilesInArea.h + tilesInTask.h - 1) / tilesInTask.h); +} + +// Calculate the number of tiles to process per task, taking into account the maximum number of +// tasks. It prefers to increase horizontally for better locality of reference. +dng_point num_tiles_per_task(const int maxTasks, + const dng_point &tilesInArea) { + dng_point tilesInTask = {1, 1}; + while (num_tasks_required(tilesInTask, tilesInArea) > maxTasks) { + if (tilesInTask.h < tilesInArea.h) { + ++tilesInTask.h; + } else if (tilesInTask.v < tilesInArea.v) { + ++tilesInTask.v; + } else { + ThrowProgramError("num_tiles_per_task calculation is wrong."); + } + } + return tilesInTask; +} + +std::vector compute_task_areas(const int maxTasks, const dng_rect& area, + const dng_point& tileSize) { + std::vector taskAreas; + const dng_point tilesInArea = num_tiles_in_area(area.Size(), tileSize); + const dng_point tilesPerTask = num_tiles_per_task(maxTasks, tilesInArea); + const dng_point taskAreaSize = {tilesPerTask.v * tileSize.v, + tilesPerTask.h * tileSize.h}; + for (int v = 0; v < tilesInArea.v; v += tilesPerTask.v) { + for (int h = 0; h < tilesInArea.h; h += tilesPerTask.h) { + dng_rect taskArea; + taskArea.t = area.t + v * tileSize.v; + taskArea.l = area.l + h * tileSize.h; + taskArea.b = Min_int32(taskArea.t + taskAreaSize.v, area.b); + taskArea.r = Min_int32(taskArea.l + taskAreaSize.h, area.r); + + taskAreas.push_back(taskArea); + } + } + return taskAreas; +} + +class SkDngHost : public dng_host { +public: + explicit SkDngHost(dng_memory_allocator* allocater) : dng_host(allocater) {} + + void PerformAreaTask(dng_area_task& task, const dng_rect& area) override { + // The area task gets split up into max_tasks sub-tasks. The max_tasks is defined by the + // dng-sdks default implementation of dng_area_task::MaxThreads() which returns 8 or 32 + // sub-tasks depending on the architecture. + const int maxTasks = static_cast(task.MaxThreads()); + + SkTaskGroup taskGroup; + + // tileSize is typically 256x256 + const dng_point tileSize(task.FindTileSize(area)); + const std::vector taskAreas = compute_task_areas(maxTasks, area, tileSize); + const int numTasks = static_cast(taskAreas.size()); + + SkMutex mutex; + SkTArray exceptions; + task.Start(numTasks, tileSize, &Allocator(), Sniffer()); + for (int taskIndex = 0; taskIndex < numTasks; ++taskIndex) { + taskGroup.add([&mutex, &exceptions, &task, this, taskIndex, taskAreas, tileSize] { + try { + task.ProcessOnThread(taskIndex, taskAreas[taskIndex], tileSize, this->Sniffer()); + } catch (dng_exception& exception) { + SkAutoMutexAcquire lock(mutex); + exceptions.push_back(exception); + } catch (...) { + SkAutoMutexAcquire lock(mutex); + exceptions.push_back(dng_exception(dng_error_unknown)); + } + }); + } + + taskGroup.wait(); + task.Finish(numTasks); + + // Currently we only re-throw the first catched exception. + if (!exceptions.empty()) { + Throw_dng_error(exceptions.front().ErrorCode(), nullptr, nullptr); + } + } + + uint32 PerformAreaTaskThreads() override { + // FIXME: Need to get the real amount of available threads used in the SkTaskGroup. + return kMaxMPThreads; + } + +private: + typedef dng_host INHERITED; +}; + +// T must be unsigned type. +template +bool safe_add_to_size_t(T arg1, T arg2, size_t* result) { + SkASSERT(arg1 >= 0); + SkASSERT(arg2 >= 0); + if (arg1 >= 0 && arg2 <= std::numeric_limits::max() - arg1) { + T sum = arg1 + arg2; + if (sum <= std::numeric_limits::max()) { + *result = static_cast(sum); + return true; + } + } + return false; +} + +class SkDngMemoryAllocator : public dng_memory_allocator { +public: + ~SkDngMemoryAllocator() override {} + + dng_memory_block* Allocate(uint32 size) override { + // To avoid arbitary allocation requests which might lead to out-of-memory, limit the + // amount of memory that can be allocated at once. The memory limit is based on experiments + // and supposed to be sufficient for all valid DNG images. + if (size > 300 * 1024 * 1024) { // 300 MB + ThrowMemoryFull(); + } + return dng_memory_allocator::Allocate(size); + } +}; + +bool is_asset_stream(const SkStream& stream) { + return stream.hasLength() && stream.hasPosition(); +} + +} // namespace + +class SkRawStream { +public: + virtual ~SkRawStream() {} + + /* + * Gets the length of the stream. Depending on the type of stream, this may require reading to + * the end of the stream. + */ + virtual uint64 getLength() = 0; + + virtual bool read(void* data, size_t offset, size_t length) = 0; + + /* + * Creates an SkMemoryStream from the offset with size. + * Note: for performance reason, this function is destructive to the SkRawStream. One should + * abandon current object after the function call. + */ + virtual SkMemoryStream* transferBuffer(size_t offset, size_t size) = 0; +}; + +class SkRawLimitedDynamicMemoryWStream : public SkDynamicMemoryWStream { +public: + virtual ~SkRawLimitedDynamicMemoryWStream() {} + + bool write(const void* buffer, size_t size) override { + size_t newSize; + if (!safe_add_to_size_t(this->bytesWritten(), size, &newSize) || + newSize > kMaxStreamSize) + { + SkCodecPrintf("Error: Stream size exceeds the limit.\n"); + return false; + } + return this->INHERITED::write(buffer, size); + } + +private: + // Most of valid RAW images will not be larger than 100MB. This limit is helpful to avoid + // streaming too large data chunk. We can always adjust the limit here if we need. + const size_t kMaxStreamSize = 100 * 1024 * 1024; // 100MB + + typedef SkDynamicMemoryWStream INHERITED; +}; + +// Note: the maximum buffer size is 100MB (limited by SkRawLimitedDynamicMemoryWStream). +class SkRawBufferedStream : public SkRawStream { +public: + // Will take the ownership of the stream. + explicit SkRawBufferedStream(SkStream* stream) + : fStream(stream) + , fWholeStreamRead(false) + { + // Only use SkRawBufferedStream when the stream is not an asset stream. + SkASSERT(!is_asset_stream(*stream)); + } + + ~SkRawBufferedStream() override {} + + uint64 getLength() override { + if (!this->bufferMoreData(kReadToEnd)) { // read whole stream + ThrowReadFile(); + } + return fStreamBuffer.bytesWritten(); + } + + bool read(void* data, size_t offset, size_t length) override { + if (length == 0) { + return true; + } + + size_t sum; + if (!safe_add_to_size_t(offset, length, &sum)) { + return false; + } + + return this->bufferMoreData(sum) && fStreamBuffer.read(data, offset, length); + } + + SkMemoryStream* transferBuffer(size_t offset, size_t size) override { + SkAutoTUnref data(SkData::NewUninitialized(size)); + if (offset > fStreamBuffer.bytesWritten()) { + // If the offset is not buffered, read from fStream directly and skip the buffering. + const size_t skipLength = offset - fStreamBuffer.bytesWritten(); + if (fStream->skip(skipLength) != skipLength) { + return nullptr; + } + const size_t bytesRead = fStream->read(data->writable_data(), size); + if (bytesRead < size) { + data.reset(SkData::NewSubset(data.get(), 0, bytesRead)); + } + } else { + const size_t alreadyBuffered = SkTMin(fStreamBuffer.bytesWritten() - offset, size); + if (alreadyBuffered > 0 && + !fStreamBuffer.read(data->writable_data(), offset, alreadyBuffered)) { + return nullptr; + } + + const size_t remaining = size - alreadyBuffered; + if (remaining) { + auto* dst = static_cast(data->writable_data()) + alreadyBuffered; + const size_t bytesRead = fStream->read(dst, remaining); + size_t newSize; + if (bytesRead < remaining) { + if (!safe_add_to_size_t(alreadyBuffered, bytesRead, &newSize)) { + return nullptr; + } + data.reset(SkData::NewSubset(data.get(), 0, newSize)); + } + } + } + return new SkMemoryStream(data); + } + +private: + // Note: if the newSize == kReadToEnd (0), this function will read to the end of stream. + bool bufferMoreData(size_t newSize) { + if (newSize == kReadToEnd) { + if (fWholeStreamRead) { // already read-to-end. + return true; + } + + // TODO: optimize for the special case when the input is SkMemoryStream. + return SkStreamCopy(&fStreamBuffer, fStream.get()); + } + + if (newSize <= fStreamBuffer.bytesWritten()) { // already buffered to newSize + return true; + } + if (fWholeStreamRead) { // newSize is larger than the whole stream. + return false; + } + + // Try to read at least 8192 bytes to avoid to many small reads. + const size_t kMinSizeToRead = 8192; + const size_t sizeRequested = newSize - fStreamBuffer.bytesWritten(); + const size_t sizeToRead = SkTMax(kMinSizeToRead, sizeRequested); + SkAutoSTMalloc tempBuffer(sizeToRead); + const size_t bytesRead = fStream->read(tempBuffer.get(), sizeToRead); + if (bytesRead < sizeRequested) { + return false; + } + return fStreamBuffer.write(tempBuffer.get(), bytesRead); + } + + SkAutoTDelete fStream; + bool fWholeStreamRead; + + // Use a size-limited stream to avoid holding too huge buffer. + SkRawLimitedDynamicMemoryWStream fStreamBuffer; + + const size_t kReadToEnd = 0; +}; + +class SkRawAssetStream : public SkRawStream { +public: + // Will take the ownership of the stream. + explicit SkRawAssetStream(SkStream* stream) + : fStream(stream) + { + // Only use SkRawAssetStream when the stream is an asset stream. + SkASSERT(is_asset_stream(*stream)); + } + + ~SkRawAssetStream() override {} + + uint64 getLength() override { + return fStream->getLength(); + } + + + bool read(void* data, size_t offset, size_t length) override { + if (length == 0) { + return true; + } + + size_t sum; + if (!safe_add_to_size_t(offset, length, &sum)) { + return false; + } + + return fStream->seek(offset) && (fStream->read(data, length) == length); + } + + SkMemoryStream* transferBuffer(size_t offset, size_t size) override { + if (fStream->getLength() < offset) { + return nullptr; + } + + size_t sum; + if (!safe_add_to_size_t(offset, size, &sum)) { + return nullptr; + } + + // This will allow read less than the requested "size", because the JPEG codec wants to + // handle also a partial JPEG file. + const size_t bytesToRead = SkTMin(sum, fStream->getLength()) - offset; + if (bytesToRead == 0) { + return nullptr; + } + + if (fStream->getMemoryBase()) { // directly copy if getMemoryBase() is available. + SkAutoTUnref data(SkData::NewWithCopy( + static_cast(fStream->getMemoryBase()) + offset, bytesToRead)); + fStream.reset(); + return new SkMemoryStream(data); + } else { + SkAutoTUnref data(SkData::NewUninitialized(bytesToRead)); + if (!fStream->seek(offset)) { + return nullptr; + } + const size_t bytesRead = fStream->read(data->writable_data(), bytesToRead); + if (bytesRead < bytesToRead) { + data.reset(SkData::NewSubset(data.get(), 0, bytesRead)); + } + return new SkMemoryStream(data); + } + } +private: + SkAutoTDelete fStream; +}; + +class SkPiexStream : public ::piex::StreamInterface { +public: + // Will NOT take the ownership of the stream. + explicit SkPiexStream(SkRawStream* stream) : fStream(stream) {} + + ~SkPiexStream() override {} + + ::piex::Error GetData(const size_t offset, const size_t length, + uint8* data) override { + return fStream->read(static_cast(data), offset, length) ? + ::piex::Error::kOk : ::piex::Error::kFail; + } + +private: + SkRawStream* fStream; +}; + +class SkDngStream : public dng_stream { +public: + // Will NOT take the ownership of the stream. + SkDngStream(SkRawStream* stream) : fStream(stream) {} + + ~SkDngStream() override {} + + uint64 DoGetLength() override { return fStream->getLength(); } + + void DoRead(void* data, uint32 count, uint64 offset) override { + size_t sum; + if (!safe_add_to_size_t(static_cast(count), offset, &sum) || + !fStream->read(data, static_cast(offset), static_cast(count))) { + ThrowReadFile(); + } + } + +private: + SkRawStream* fStream; +}; + +class SkDngImage { +public: + /* + * Initializes the object with the information from Piex in a first attempt. This way it can + * save time and storage to obtain the DNG dimensions and color filter array (CFA) pattern + * which is essential for the demosaicing of the sensor image. + * Note: this will take the ownership of the stream. + */ + static SkDngImage* NewFromStream(SkRawStream* stream) { + SkAutoTDelete dngImage(new SkDngImage(stream)); + if (!dngImage->isTiffHeaderValid()) { + return nullptr; + } + + if (!dngImage->initFromPiex()) { + if (!dngImage->readDng()) { + return nullptr; + } + } + + return dngImage.release(); + } + + /* + * Renders the DNG image to the size. The DNG SDK only allows scaling close to integer factors + * down to 80 pixels on the short edge. The rendered image will be close to the specified size, + * but there is no guarantee that any of the edges will match the requested size. E.g. + * 100% size: 4000 x 3000 + * requested size: 1600 x 1200 + * returned size could be: 2000 x 1500 + */ + dng_image* render(int width, int height) { + if (!fHost || !fInfo || !fNegative || !fDngStream) { + if (!this->readDng()) { + return nullptr; + } + } + + // DNG SDK preserves the aspect ratio, so it only needs to know the longer dimension. + const int preferredSize = SkTMax(width, height); + try { + // render() takes ownership of fHost, fInfo, fNegative and fDngStream when available. + SkAutoTDelete host(fHost.release()); + SkAutoTDelete info(fInfo.release()); + SkAutoTDelete negative(fNegative.release()); + SkAutoTDelete dngStream(fDngStream.release()); + + host->SetPreferredSize(preferredSize); + host->ValidateSizes(); + + negative->ReadStage1Image(*host, *dngStream, *info); + + if (info->fMaskIndex != -1) { + negative->ReadTransparencyMask(*host, *dngStream, *info); + } + + negative->ValidateRawImageDigest(*host); + if (negative->IsDamaged()) { + return nullptr; + } + + const int32 kMosaicPlane = -1; + negative->BuildStage2Image(*host); + negative->BuildStage3Image(*host, kMosaicPlane); + + dng_render render(*host, *negative); + render.SetFinalSpace(dng_space_sRGB::Get()); + render.SetFinalPixelType(ttByte); + + dng_point stage3_size = negative->Stage3Image()->Size(); + render.SetMaximumSize(SkTMax(stage3_size.h, stage3_size.v)); + + return render.Render(); + } catch (...) { + return nullptr; + } + } + + const SkImageInfo& getImageInfo() const { + return fImageInfo; + } + + bool isScalable() const { + return fIsScalable; + } + + bool isXtransImage() const { + return fIsXtransImage; + } + +private: + // Quick check if the image contains a valid TIFF header as requested by DNG format. + bool isTiffHeaderValid() const { + const size_t kHeaderSize = 4; + SkAutoSTMalloc header(kHeaderSize); + if (!fStream->read(header.get(), 0 /* offset */, kHeaderSize)) { + return false; + } + + // Check if the header is valid (endian info and magic number "42"). + bool littleEndian; + if (!is_valid_endian_marker(header, &littleEndian)) { + return false; + } + + return 0x2A == get_endian_short(header + 2, littleEndian); + } + + void init(const int width, const int height, const dng_point& cfaPatternSize) { + fImageInfo = SkImageInfo::Make(width, height, kN32_SkColorType, kOpaque_SkAlphaType); + + // The DNG SDK scales only during demosaicing, so scaling is only possible when + // a mosaic info is available. + fIsScalable = cfaPatternSize.v != 0 && cfaPatternSize.h != 0; + fIsXtransImage = fIsScalable ? (cfaPatternSize.v == 6 && cfaPatternSize.h == 6) : false; + } + + bool initFromPiex() { + // Does not take the ownership of rawStream. + SkPiexStream piexStream(fStream.get()); + ::piex::PreviewImageData imageData; + if (::piex::IsRaw(&piexStream) + && ::piex::GetPreviewImageData(&piexStream, &imageData) == ::piex::Error::kOk) + { + // Verify the size information, as it is only optional information for PIEX. + if (imageData.full_width == 0 || imageData.full_height == 0) { + return false; + } + + dng_point cfaPatternSize(imageData.cfa_pattern_dim[1], imageData.cfa_pattern_dim[0]); + this->init(static_cast(imageData.full_width), + static_cast(imageData.full_height), cfaPatternSize); + return true; + } + return false; + } + + bool readDng() { + try { + // Due to the limit of DNG SDK, we need to reset host and info. + fHost.reset(new SkDngHost(&fAllocator)); + fInfo.reset(new dng_info); + fDngStream.reset(new SkDngStream(fStream)); + + fHost->ValidateSizes(); + fInfo->Parse(*fHost, *fDngStream); + fInfo->PostParse(*fHost); + if (!fInfo->IsValidDNG()) { + return false; + } + + fNegative.reset(fHost->Make_dng_negative()); + fNegative->Parse(*fHost, *fDngStream, *fInfo); + fNegative->PostParse(*fHost, *fDngStream, *fInfo); + fNegative->SynchronizeMetadata(); + + dng_point cfaPatternSize(0, 0); + if (fNegative->GetMosaicInfo() != nullptr) { + cfaPatternSize = fNegative->GetMosaicInfo()->fCFAPatternSize; + } + this->init(static_cast(fNegative->DefaultCropSizeH().As_real64()), + static_cast(fNegative->DefaultCropSizeV().As_real64()), + cfaPatternSize); + return true; + } catch (...) { + return false; + } + } + + SkDngImage(SkRawStream* stream) + : fStream(stream) {} + + SkDngMemoryAllocator fAllocator; + SkAutoTDelete fStream; + SkAutoTDelete fHost; + SkAutoTDelete fInfo; + SkAutoTDelete fNegative; + SkAutoTDelete fDngStream; + + SkImageInfo fImageInfo; + bool fIsScalable; + bool fIsXtransImage; +}; + +/* + * Tries to handle the image with PIEX. If PIEX returns kOk and finds the preview image, create a + * SkJpegCodec. If PIEX returns kFail, then the file is invalid, return nullptr. In other cases, + * fallback to create SkRawCodec for DNG images. + */ +SkCodec* SkRawCodec::NewFromStream(SkStream* stream) { + SkAutoTDelete rawStream; + if (is_asset_stream(*stream)) { + rawStream.reset(new SkRawAssetStream(stream)); + } else { + rawStream.reset(new SkRawBufferedStream(stream)); + } + + // Does not take the ownership of rawStream. + SkPiexStream piexStream(rawStream.get()); + ::piex::PreviewImageData imageData; + if (::piex::IsRaw(&piexStream)) { + ::piex::Error error = ::piex::GetPreviewImageData(&piexStream, &imageData); + + if (error == ::piex::Error::kOk && imageData.preview.length > 0) { + // transferBuffer() is destructive to the rawStream. Abandon the rawStream after this + // function call. + // FIXME: one may avoid the copy of memoryStream and use the buffered rawStream. + SkMemoryStream* memoryStream = + rawStream->transferBuffer(imageData.preview.offset, imageData.preview.length); + return memoryStream ? SkJpegCodec::NewFromStream(memoryStream) : nullptr; + } else if (error == ::piex::Error::kFail) { + return nullptr; + } + } + + // Takes the ownership of the rawStream. + SkAutoTDelete dngImage(SkDngImage::NewFromStream(rawStream.release())); + if (!dngImage) { + return nullptr; + } + + return new SkRawCodec(dngImage.release()); +} + +SkCodec::Result SkRawCodec::onGetPixels(const SkImageInfo& requestedInfo, void* dst, + size_t dstRowBytes, const Options& options, + SkPMColor ctable[], int* ctableCount, + int* rowsDecoded) { + if (!conversion_possible(requestedInfo, this->getInfo())) { + SkCodecPrintf("Error: cannot convert input type to output type.\n"); + return kInvalidConversion; + } + + SkAutoTDelete swizzler(SkSwizzler::CreateSwizzler( + SkSwizzler::kRGB, nullptr, requestedInfo, options)); + SkASSERT(swizzler); + + const int width = requestedInfo.width(); + const int height = requestedInfo.height(); + SkAutoTDelete image(fDngImage->render(width, height)); + if (!image) { + return kInvalidInput; + } + + // Because the DNG SDK can not guarantee to render to requested size, we allow a small + // difference. Only the overlapping region will be converted. + const float maxDiffRatio = 1.03f; + const dng_point& imageSize = image->Size(); + if (imageSize.h / width > maxDiffRatio || imageSize.h < width || + imageSize.v / height > maxDiffRatio || imageSize.v < height) { + return SkCodec::kInvalidScale; + } + + void* dstRow = dst; + SkAutoTMalloc srcRow(width * 3); + + dng_pixel_buffer buffer; + buffer.fData = &srcRow[0]; + buffer.fPlane = 0; + buffer.fPlanes = 3; + buffer.fColStep = buffer.fPlanes; + buffer.fPlaneStep = 1; + buffer.fPixelType = ttByte; + buffer.fPixelSize = sizeof(uint8_t); + buffer.fRowStep = width * 3; + + for (int i = 0; i < height; ++i) { + buffer.fArea = dng_rect(i, 0, i + 1, width); + + try { + image->Get(buffer, dng_image::edge_zero); + } catch (...) { + *rowsDecoded = i; + return kIncompleteInput; + } + + swizzler->swizzle(dstRow, &srcRow[0]); + dstRow = SkTAddOffset(dstRow, dstRowBytes); + } + return kSuccess; +} + +SkISize SkRawCodec::onGetScaledDimensions(float desiredScale) const { + SkASSERT(desiredScale <= 1.f); + + const SkISize dim = this->getInfo().dimensions(); + SkASSERT(dim.fWidth != 0 && dim.fHeight != 0); + + if (!fDngImage->isScalable()) { + return dim; + } + + // Limits the minimum size to be 80 on the short edge. + const float shortEdge = static_cast(SkTMin(dim.fWidth, dim.fHeight)); + if (desiredScale < 80.f / shortEdge) { + desiredScale = 80.f / shortEdge; + } + + // For Xtrans images, the integer-factor scaling does not support the half-size scaling case + // (stronger downscalings are fine). In this case, returns the factor "3" scaling instead. + if (fDngImage->isXtransImage() && desiredScale > 1.f / 3.f && desiredScale < 1.f) { + desiredScale = 1.f / 3.f; + } + + // Round to integer-factors. + const float finalScale = std::floor(1.f/ desiredScale); + return SkISize::Make(static_cast(std::floor(dim.fWidth / finalScale)), + static_cast(std::floor(dim.fHeight / finalScale))); +} + +bool SkRawCodec::onDimensionsSupported(const SkISize& dim) { + const SkISize fullDim = this->getInfo().dimensions(); + const float fullShortEdge = static_cast(SkTMin(fullDim.fWidth, fullDim.fHeight)); + const float shortEdge = static_cast(SkTMin(dim.fWidth, dim.fHeight)); + + SkISize sizeFloor = this->onGetScaledDimensions(1.f / std::floor(fullShortEdge / shortEdge)); + SkISize sizeCeil = this->onGetScaledDimensions(1.f / std::ceil(fullShortEdge / shortEdge)); + return sizeFloor == dim || sizeCeil == dim; +} + +SkRawCodec::~SkRawCodec() {} + +SkRawCodec::SkRawCodec(SkDngImage* dngImage) + : INHERITED(dngImage->getImageInfo(), nullptr) + , fDngImage(dngImage) {} diff --git a/gfx/skia/skia/src/codec/SkRawCodec.h b/gfx/skia/skia/src/codec/SkRawCodec.h new file mode 100644 index 0000000000..3ca7b9c17e --- /dev/null +++ b/gfx/skia/skia/src/codec/SkRawCodec.h @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkRawCodec_DEFINED +#define SkRawCodec_DEFINED + +#include "SkCodec.h" +#include "SkColorSpace.h" +#include "SkImageInfo.h" +#include "SkTypes.h" + +class SkDngImage; +class SkStream; + +/* + * + * This class implements the decoding for RAW images + * + */ +class SkRawCodec : public SkCodec { +public: + + /* + * Creates a RAW decoder + * Takes ownership of the stream + */ + static SkCodec* NewFromStream(SkStream*); + + ~SkRawCodec() override; + +protected: + + Result onGetPixels(const SkImageInfo& dstInfo, void* dst, size_t dstRowBytes, const Options&, + SkPMColor*, int*, int*) override; + + SkEncodedFormat onGetEncodedFormat() const override { + return kDNG_SkEncodedFormat; + } + + SkISize onGetScaledDimensions(float desiredScale) const override; + + bool onDimensionsSupported(const SkISize&) override; + +private: + + /* + * Creates an instance of the decoder + * Called only by NewFromStream, takes ownership of dngImage. + */ + SkRawCodec(SkDngImage* dngImage); + + SkAutoTDelete fDngImage; + + typedef SkCodec INHERITED; +}; + +#endif diff --git a/gfx/skia/skia/src/codec/SkSampledCodec.cpp b/gfx/skia/skia/src/codec/SkSampledCodec.cpp index e52470505d..49c939c1f8 100644 --- a/gfx/skia/skia/src/codec/SkSampledCodec.cpp +++ b/gfx/skia/skia/src/codec/SkSampledCodec.cpp @@ -253,8 +253,7 @@ SkCodec::Result SkSampledCodec::sampledDecode(const SkImageInfo& info, void* pix // We handle filling uninitialized memory here instead of using this->codec(). // this->codec() does not know that we are sampling. - const uint32_t fillValue = this->codec()->getFillValue(info.colorType(), - info.alphaType()); + const uint32_t fillValue = this->codec()->getFillValue(info.colorType()); const SkImageInfo fillInfo = info.makeWH(info.width(), 1); for (; y < nativeSize.height(); y++) { int srcY = this->codec()->outputScanline(y); diff --git a/gfx/skia/skia/src/codec/SkSwizzler.cpp b/gfx/skia/skia/src/codec/SkSwizzler.cpp index 74d6c7f87f..133736879f 100644 --- a/gfx/skia/skia/src/codec/SkSwizzler.cpp +++ b/gfx/skia/skia/src/codec/SkSwizzler.cpp @@ -11,19 +11,44 @@ #include "SkSwizzler.h" #include "SkTemplates.h" -// samples the row. Does not do anything else but sampling -static void sample565(void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, - int width, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]){ +static void copy(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + memcpy(dst, src + offset, width * bpp); +} + +static void sample1(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { src += offset; - uint16_t* SK_RESTRICT dst = (uint16_t*) dstRow; + uint8_t* dst8 = (uint8_t*) dst; for (int x = 0; x < width; x++) { - dst[x] = src[1] << 8 | src[0]; + dst8[x] = *src; src += deltaSrc; } } -// TODO (msarett): Investigate SIMD optimizations for swizzle routines. +static void sample2(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + src += offset; + uint16_t* dst16 = (uint16_t*) dst; + for (int x = 0; x < width; x++) { + dst16[x] = *((const uint16_t*) src); + src += deltaSrc; + } +} + +static void sample4(void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + src += offset; + uint32_t* dst32 = (uint32_t*) dst; + for (int x = 0; x < width; x++) { + dst32[x] = *((const uint32_t*) src); + src += deltaSrc; + } +} // kBit // These routines exclusively choose between white and black @@ -192,22 +217,6 @@ static void swizzle_small_index_to_n32( // kIndex -static void swizzle_index_to_index( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { - - src += offset; - uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; - if (1 == deltaSrc) { - memcpy(dst, src, dstWidth); - } else { - for (int x = 0; x < dstWidth; x++) { - dst[x] = *src; - src += deltaSrc; - } - } -} - static void swizzle_index_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -261,20 +270,17 @@ static void swizzle_gray_to_n32( } } -static void swizzle_gray_to_gray( - void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, - int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { +static void fast_swizzle_gray_to_n32( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { - src += offset; - uint8_t* SK_RESTRICT dst = (uint8_t*) dstRow; - if (1 == deltaSrc) { - memcpy(dstRow, src, dstWidth); - } else { - for (int x = 0; x < dstWidth; x++) { - dst[x] = src[0]; - src += deltaSrc; - } - } + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + + // Note that there is no need to distinguish between RGB and BGR. + // Each color channel will get the same value. + SkOpts::gray_to_RGB1((uint32_t*) dst, src + offset, width); } static void swizzle_gray_to_565( @@ -289,6 +295,59 @@ static void swizzle_gray_to_565( } } +// kGrayAlpha + +static void swizzle_grayalpha_to_n32_unpremul( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + + src += offset; + SkPMColor* dst32 = (SkPMColor*) dst; + for (int x = 0; x < width; x++) { + dst32[x] = SkPackARGB32NoCheck(src[1], src[0], src[0], src[0]); + src += deltaSrc; + } +} + +static void fast_swizzle_grayalpha_to_n32_unpremul( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + + // Note that there is no need to distinguish between RGB and BGR. + // Each color channel will get the same value. + SkOpts::grayA_to_RGBA((uint32_t*) dst, src + offset, width); +} + +static void swizzle_grayalpha_to_n32_premul( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + + src += offset; + SkPMColor* dst32 = (SkPMColor*) dst; + for (int x = 0; x < width; x++) { + uint8_t pmgray = SkMulDiv255Round(src[1], src[0]); + dst32[x] = SkPackARGB32NoCheck(src[1], pmgray, pmgray, pmgray); + src += deltaSrc; + } +} + +static void fast_swizzle_grayalpha_to_n32_premul( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + + // Note that there is no need to distinguish between rgb and bgr. + // Each color channel will get the same value. + SkOpts::grayA_to_rgbA((uint32_t*) dst, src + offset, width); +} + // kBGRX static void swizzle_bgrx_to_n32( @@ -330,6 +389,21 @@ static void swizzle_bgra_to_n32_unpremul( } } +static void fast_swizzle_bgra_to_n32_unpremul( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + +#ifdef SK_PMCOLOR_IS_RGBA + SkOpts::RGBA_to_BGRA((uint32_t*) dst, src + offset, width); +#else + memcpy(dst, src + offset, width * bpp); +#endif +} + static void swizzle_bgra_to_n32_premul( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -343,20 +417,51 @@ static void swizzle_bgra_to_n32_premul( } } -// kRGBX -static void swizzle_rgbx_to_n32( +static void fast_swizzle_bgra_to_n32_premul( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + +#ifdef SK_PMCOLOR_IS_RGBA + SkOpts::RGBA_to_bgrA((uint32_t*) dst, src + offset, width); +#else + SkOpts::RGBA_to_rgbA((uint32_t*) dst, src + offset, width); +#endif +} + +// kRGB + +static void swizzle_rgb_to_n32( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { src += offset; SkPMColor* SK_RESTRICT dst = (SkPMColor*)dstRow; for (int x = 0; x < dstWidth; x++) { - dst[x] = SkPackARGB32(0xFF, src[0], src[1], src[2]); + dst[x] = SkPackARGB32NoCheck(0xFF, src[0], src[1], src[2]); src += deltaSrc; } } -static void swizzle_rgbx_to_565( +static void fast_swizzle_rgb_to_n32( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, + int offset, const SkPMColor ctable[]) { + + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + +#ifdef SK_PMCOLOR_IS_RGBA + SkOpts::RGB_to_RGB1((uint32_t*) dst, src + offset, width); +#else + SkOpts::RGB_to_BGR1((uint32_t*) dst, src + offset, width); +#endif +} + +static void swizzle_rgb_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bytesPerPixel, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -369,6 +474,7 @@ static void swizzle_rgbx_to_565( } // kRGBA + static void swizzle_rgba_to_n32_premul( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -391,9 +497,9 @@ static void fast_swizzle_rgba_to_n32_premul( SkASSERT(deltaSrc == bpp); #ifdef SK_PMCOLOR_IS_RGBA - SkOpts::premul_xxxa((uint32_t*) dst, (const uint32_t*) (src + offset), width); + SkOpts::RGBA_to_rgbA((uint32_t*) dst, src + offset, width); #else - SkOpts::premul_swaprb_xxxa((uint32_t*) dst, (const uint32_t*) (src + offset), width); + SkOpts::RGBA_to_bgrA((uint32_t*) dst, src + offset, width); #endif } @@ -410,6 +516,21 @@ static void swizzle_rgba_to_n32_unpremul( } } +static void fast_swizzle_rgba_to_n32_unpremul( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + +#ifdef SK_PMCOLOR_IS_RGBA + memcpy(dst, src + offset, width * bpp); +#else + SkOpts::RGBA_to_BGRA((uint32_t*) dst, src + offset, width); +#endif +} + // kCMYK // // CMYK is stored as four bytes per pixel. @@ -471,6 +592,21 @@ static void swizzle_cmyk_to_n32( } } +static void fast_swizzle_cmyk_to_n32( + void* dst, const uint8_t* src, int width, int bpp, int deltaSrc, int offset, + const SkPMColor ctable[]) { + + // This function must not be called if we are sampling. If we are not + // sampling, deltaSrc should equal bpp. + SkASSERT(deltaSrc == bpp); + +#ifdef SK_PMCOLOR_IS_RGBA + SkOpts::inverted_CMYK_to_RGB1((uint32_t*) dst, src + offset, width); +#else + SkOpts::inverted_CMYK_to_BGR1((uint32_t*) dst, src + offset, width); +#endif +} + static void swizzle_cmyk_to_565( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { @@ -487,6 +623,25 @@ static void swizzle_cmyk_to_565( } } +template +void SkSwizzler::SkipLeadingGrayAlphaZerosThen( + void* dst, const uint8_t* src, int width, + int bpp, int deltaSrc, int offset, const SkPMColor ctable[]) { + SkASSERT(!ctable); + + const uint16_t* src16 = (const uint16_t*) (src + offset); + uint32_t* dst32 = (uint32_t*) dst; + + // This may miss opportunities to skip when the output is premultiplied, + // e.g. for a src pixel 0x00FF which is not zero but becomes zero after premultiplication. + while (width > 0 && *src16 == 0x0000) { + width--; + dst32++; + src16 += deltaSrc / 2; + } + proc(dst32, (const uint8_t*)src16, width, bpp, deltaSrc, 0, ctable); +} + template void SkSwizzler::SkipLeading8888ZerosThen( void* SK_RESTRICT dstRow, const uint8_t* SK_RESTRICT src, int dstWidth, @@ -573,7 +728,8 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, proc = &swizzle_index_to_565; break; case kIndex_8_SkColorType: - proc = &swizzle_index_to_index; + proc = &sample1; + fastProc = © break; default: break; @@ -583,9 +739,11 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_gray_to_n32; + fastProc = &fast_swizzle_gray_to_n32; break; case kGray_8_SkColorType: - proc = &swizzle_gray_to_gray; + proc = &sample1; + fastProc = © break; case kRGB_565_SkColorType: proc = &swizzle_gray_to_565; @@ -594,6 +752,34 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, break; } break; + case kGrayAlpha: + switch (dstInfo.colorType()) { + case kN32_SkColorType: + if (dstInfo.alphaType() == kUnpremul_SkAlphaType) { + if (SkCodec::kYes_ZeroInitialized == zeroInit) { + proc = &SkipLeadingGrayAlphaZerosThen + ; + fastProc = &SkipLeadingGrayAlphaZerosThen + ; + } else { + proc = &swizzle_grayalpha_to_n32_unpremul; + fastProc = &fast_swizzle_grayalpha_to_n32_unpremul; + } + } else { + if (SkCodec::kYes_ZeroInitialized == zeroInit) { + proc = &SkipLeadingGrayAlphaZerosThen; + fastProc = &SkipLeadingGrayAlphaZerosThen + ; + } else { + proc = &swizzle_grayalpha_to_n32_premul; + fastProc = &fast_swizzle_grayalpha_to_n32_premul; + } + } + break; + default: + break; + } + break; case kBGR: case kBGRX: switch (dstInfo.colorType()) { @@ -610,28 +796,37 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, case kBGRA: switch (dstInfo.colorType()) { case kN32_SkColorType: - switch (dstInfo.alphaType()) { - case kUnpremul_SkAlphaType: + if (dstInfo.alphaType() == kUnpremul_SkAlphaType) { + if (SkCodec::kYes_ZeroInitialized == zeroInit) { + proc = &SkipLeading8888ZerosThen; + fastProc = &SkipLeading8888ZerosThen; + } else { proc = &swizzle_bgra_to_n32_unpremul; - break; - case kPremul_SkAlphaType: + fastProc = &fast_swizzle_bgra_to_n32_unpremul; + } + } else { + if (SkCodec::kYes_ZeroInitialized == zeroInit) { + proc = &SkipLeading8888ZerosThen; + fastProc = &SkipLeading8888ZerosThen; + } else { proc = &swizzle_bgra_to_n32_premul; - break; - default: - break; + fastProc = &fast_swizzle_bgra_to_n32_premul; + } } break; default: break; } break; - case kRGBX: + case kRGB: switch (dstInfo.colorType()) { case kN32_SkColorType: - proc = &swizzle_rgbx_to_n32; + proc = &swizzle_rgb_to_n32; + fastProc = &fast_swizzle_rgb_to_n32; break; case kRGB_565_SkColorType: - proc = &swizzle_rgbx_to_565; + proc = &swizzle_rgb_to_565; + break; default: break; } @@ -642,8 +837,10 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, if (dstInfo.alphaType() == kUnpremul_SkAlphaType) { if (SkCodec::kYes_ZeroInitialized == zeroInit) { proc = &SkipLeading8888ZerosThen; + fastProc = &SkipLeading8888ZerosThen; } else { proc = &swizzle_rgba_to_n32_unpremul; + fastProc = &fast_swizzle_rgba_to_n32_unpremul; } } else { if (SkCodec::kYes_ZeroInitialized == zeroInit) { @@ -659,28 +856,11 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, break; } break; - case kRGB: - switch (dstInfo.colorType()) { - case kN32_SkColorType: - proc = &swizzle_rgbx_to_n32; - break; - default: - break; - } - break; - case kRGB_565: - switch (dstInfo.colorType()) { - case kRGB_565_SkColorType: - proc = &sample565; - break; - default: - break; - } - break; case kCMYK: switch (dstInfo.colorType()) { case kN32_SkColorType: proc = &swizzle_cmyk_to_n32; + fastProc = &fast_swizzle_cmyk_to_n32; break; case kRGB_565_SkColorType: proc = &swizzle_cmyk_to_565; @@ -689,6 +869,18 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, break; } break; + case kNoOp8: + proc = &sample1; + fastProc = © + break; + case kNoOp16: + proc = sample2; + fastProc = © + break; + case kNoOp32: + proc = &sample4; + fastProc = © + break; default: break; } @@ -720,7 +912,8 @@ SkSwizzler* SkSwizzler::CreateSwizzler(SkSwizzler::SrcConfig sc, SkSwizzler::SkSwizzler(RowProc fastProc, RowProc proc, const SkPMColor* ctable, int srcOffset, int srcWidth, int dstOffset, int dstWidth, int srcBPP, int dstBPP) : fFastProc(fastProc) - , fProc(proc) + , fSlowProc(proc) + , fActualProc(fFastProc ? fFastProc : fSlowProc) , fColorTable(ctable) , fSrcOffset(srcOffset) , fDstOffset(dstOffset) @@ -736,23 +929,28 @@ SkSwizzler::SkSwizzler(RowProc fastProc, RowProc proc, const SkPMColor* ctable, {} int SkSwizzler::onSetSampleX(int sampleX) { - SkASSERT(sampleX > 0); // Surely there is an upper limit? Should there be - // way to report failure? + SkASSERT(sampleX > 0); + fSampleX = sampleX; fSrcOffsetUnits = (get_start_coord(sampleX) + fSrcOffset) * fSrcBPP; fDstOffsetBytes = (fDstOffset / sampleX) * fDstBPP; fSwizzleWidth = get_scaled_dimension(fSrcWidth, sampleX); fAllocatedWidth = get_scaled_dimension(fDstWidth, sampleX); - // The optimized swizzler routines do not (yet) support sampling. - fFastProc = nullptr; + // The optimized swizzler functions do not support sampling. Sampled swizzles + // are already fast because they skip pixels. We haven't seen a situation + // where speeding up sampling has a significant impact on total decode time. + if (1 == fSampleX && fFastProc) { + fActualProc = fFastProc; + } else { + fActualProc = fSlowProc; + } return fAllocatedWidth; } void SkSwizzler::swizzle(void* dst, const uint8_t* SK_RESTRICT src) { SkASSERT(nullptr != dst && nullptr != src); - RowProc proc = fFastProc ? fFastProc : fProc; - proc(SkTAddOffset(dst, fDstOffsetBytes), src, fSwizzleWidth, fSrcBPP, + fActualProc(SkTAddOffset(dst, fDstOffsetBytes), src, fSwizzleWidth, fSrcBPP, fSampleX * fSrcBPP, fSrcOffsetUnits, fColorTable); } diff --git a/gfx/skia/skia/src/codec/SkSwizzler.h b/gfx/skia/skia/src/codec/SkSwizzler.h index e75ab43cfe..7eebe7f981 100644 --- a/gfx/skia/skia/src/codec/SkSwizzler.h +++ b/gfx/skia/skia/src/codec/SkSwizzler.h @@ -20,20 +20,22 @@ public: */ enum SrcConfig { kUnknown, // Invalid type. - kBit, // A single bit to distinguish between white and black + kBit, // A single bit to distinguish between white and black. kGray, + kGrayAlpha, kIndex1, kIndex2, kIndex4, kIndex, kRGB, kBGR, - kRGBX, - kBGRX, + kBGRX, // The alpha channel can be anything, but the image is opaque. kRGBA, kBGRA, - kRGB_565, kCMYK, + kNoOp8, // kNoOp modes are used exclusively for sampling, subsetting, and + kNoOp16, // copying. The pixels themselves do not need to be modified. + kNoOp32, }; /* @@ -52,17 +54,19 @@ public: return 4; case kGray: case kIndex: + case kNoOp8: return 8; - case kRGB_565: + case kGrayAlpha: + case kNoOp16: return 16; case kRGB: case kBGR: return 24; - case kRGBX: case kRGBA: case kBGRX: case kBGRA: case kCMYK: + case kNoOp32: return 32; default: SkASSERT(false); @@ -160,10 +164,18 @@ private: int dstWidth, int bpp, int deltaSrc, int offset, const SkPMColor ctable[]); - // May be NULL. We will not always be able to used an optimized function. - RowProc fFastProc; - // Always non-NULL. We use this if fFastProc is NULL. - const RowProc fProc; + template + static void SkipLeadingGrayAlphaZerosThen(void* dst, const uint8_t* src, int width, int bpp, + int deltaSrc, int offset, const SkPMColor ctable[]); + + // May be NULL. We have not implemented optimized functions for all supported transforms. + const RowProc fFastProc; + // Always non-NULL. Supports sampling. + const RowProc fSlowProc; + // The actual RowProc we are using. This depends on if fFastProc is non-NULL and + // whether or not we are sampling. + RowProc fActualProc; + const SkPMColor* fColorTable; // Unowned pointer // Subset Swizzles diff --git a/gfx/skia/skia/src/codec/SkWbmpCodec.cpp b/gfx/skia/skia/src/codec/SkWbmpCodec.cpp index 4dd0f2544d..90ee322b1f 100644 --- a/gfx/skia/skia/src/codec/SkWbmpCodec.cpp +++ b/gfx/skia/skia/src/codec/SkWbmpCodec.cpp @@ -30,6 +30,19 @@ static inline void setup_color_table(SkColorType colorType, } } +static inline bool valid_color_type(SkColorType colorType, SkAlphaType alphaType) { + switch (colorType) { + case kN32_SkColorType: + case kIndex_8_SkColorType: + return true; + case kGray_8_SkColorType: + case kRGB_565_SkColorType: + return kOpaque_SkAlphaType == alphaType; + default: + return false; + } +} + static bool read_byte(SkStream* stream, uint8_t* data) { return stream->read(data, 1) == 1; @@ -84,16 +97,6 @@ bool SkWbmpCodec::onRewind() { SkSwizzler* SkWbmpCodec::initializeSwizzler(const SkImageInfo& info, const SkPMColor* ctable, const Options& opts) { - // Create the swizzler based on the desired color type - switch (info.colorType()) { - case kIndex_8_SkColorType: - case kN32_SkColorType: - case kRGB_565_SkColorType: - case kGray_8_SkColorType: - break; - default: - return nullptr; - } return SkSwizzler::CreateSwizzler(SkSwizzler::kBit, ctable, info, opts); } @@ -124,7 +127,8 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info, return kUnimplemented; } - if (!valid_alpha(info.alphaType(), this->getInfo().alphaType())) { + if (!valid_color_type(info.colorType(), info.alphaType()) || + !valid_alpha(info.alphaType(), this->getInfo().alphaType())) { return kInvalidConversion; } @@ -133,9 +137,7 @@ SkCodec::Result SkWbmpCodec::onGetPixels(const SkImageInfo& info, // Initialize the swizzler SkAutoTDelete swizzler(this->initializeSwizzler(info, ctable, options)); - if (nullptr == swizzler.get()) { - return kInvalidConversion; - } + SkASSERT(swizzler); // Perform the decode SkISize size = info.dimensions(); @@ -166,7 +168,7 @@ SkCodec* SkWbmpCodec::NewFromStream(SkStream* stream) { } SkImageInfo info = SkImageInfo::Make(size.width(), size.height(), kGray_8_SkColorType, kOpaque_SkAlphaType); - return new SkWbmpCodec(info, streamDeleter.detach()); + return new SkWbmpCodec(info, streamDeleter.release()); } int SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { @@ -181,6 +183,11 @@ int SkWbmpCodec::onGetScanlines(void* dst, int count, size_t dstRowBytes) { return count; } +bool SkWbmpCodec::onSkipScanlines(int count) { + const size_t bytesToSkip = count * fSrcRowBytes; + return this->stream()->skip(bytesToSkip) == bytesToSkip; +} + SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, SkPMColor inputColorTable[], int* inputColorCount) { if (options.fSubset) { @@ -188,7 +195,8 @@ SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, return kUnimplemented; } - if (!valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) { + if (!valid_color_type(dstInfo.colorType(), dstInfo.alphaType()) || + !valid_alpha(dstInfo.alphaType(), this->getInfo().alphaType())) { return kInvalidConversion; } @@ -202,9 +210,7 @@ SkCodec::Result SkWbmpCodec::onStartScanlineDecode(const SkImageInfo& dstInfo, // Initialize the swizzler fSwizzler.reset(this->initializeSwizzler(dstInfo, get_color_ptr(fColorTable.get()), options)); - if (nullptr == fSwizzler.get()) { - return kInvalidConversion; - } + SkASSERT(fSwizzler); fSrcBuffer.reset(fSrcRowBytes); diff --git a/gfx/skia/skia/src/codec/SkWbmpCodec.h b/gfx/skia/skia/src/codec/SkWbmpCodec.h index fb062c94d5..f43f615ed2 100644 --- a/gfx/skia/skia/src/codec/SkWbmpCodec.h +++ b/gfx/skia/skia/src/codec/SkWbmpCodec.h @@ -9,6 +9,7 @@ #define SkCodec_wbmp_DEFINED #include "SkCodec.h" +#include "SkColorSpace.h" #include "SkSwizzler.h" class SkWbmpCodec final : public SkCodec { @@ -52,8 +53,8 @@ private: SkAutoTUnref fColorTable; SkAutoTMalloc fSrcBuffer; - // FIXME: Override onSkipScanlines to avoid swizzling. int onGetScanlines(void* dst, int count, size_t dstRowBytes) override; + bool onSkipScanlines(int count) override; Result onStartScanlineDecode(const SkImageInfo& dstInfo, const Options& options, SkPMColor inputColorTable[], int* inputColorCount) override; diff --git a/gfx/skia/skia/src/codec/SkWebpCodec.cpp b/gfx/skia/skia/src/codec/SkWebpCodec.cpp index 6cfb385294..77af8e5e8f 100644 --- a/gfx/skia/skia/src/codec/SkWebpCodec.cpp +++ b/gfx/skia/skia/src/codec/SkWebpCodec.cpp @@ -77,7 +77,7 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) { SkAutoTDelete streamDeleter(stream); SkImageInfo info; if (webp_parse_header(stream, &info)) { - return new SkWebpCodec(info, streamDeleter.detach()); + return new SkWebpCodec(info, streamDeleter.release()); } return nullptr; } @@ -85,9 +85,10 @@ SkCodec* SkWebpCodec::NewFromStream(SkStream* stream) { // This version is slightly different from SkCodecPriv's version of conversion_possible. It // supports both byte orders for 8888. static bool webp_conversion_possible(const SkImageInfo& dst, const SkImageInfo& src) { - if (dst.profileType() != src.profileType()) { - return false; - } + // FIXME: skbug.com/4895 + // Currently, we ignore the SkColorProfileType on the SkImageInfo. We + // will treat the encoded data as linear regardless of what the client + // requests. if (!valid_alpha(dst.alphaType(), src.alphaType())) { return false; @@ -252,4 +253,6 @@ SkCodec::Result SkWebpCodec::onGetPixels(const SkImageInfo& dstInfo, void* dst, } SkWebpCodec::SkWebpCodec(const SkImageInfo& info, SkStream* stream) - : INHERITED(info, stream) {} + // The spec says an unmarked image is sRGB, so we return that space here. + // TODO: Add support for parsing ICC profiles from webps. + : INHERITED(info, stream, SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)) {} diff --git a/gfx/skia/skia/src/codec/SkWebpCodec.h b/gfx/skia/skia/src/codec/SkWebpCodec.h index dfc9f12ee9..2d81cf3d9c 100644 --- a/gfx/skia/skia/src/codec/SkWebpCodec.h +++ b/gfx/skia/skia/src/codec/SkWebpCodec.h @@ -9,6 +9,7 @@ #define SkWebpCodec_DEFINED #include "SkCodec.h" +#include "SkColorSpace.h" #include "SkEncodedFormat.h" #include "SkImageInfo.h" #include "SkTypes.h" diff --git a/gfx/skia/skia/src/core/Sk4x4f.h b/gfx/skia/skia/src/core/Sk4x4f.h new file mode 100644 index 0000000000..9bd91973d3 --- /dev/null +++ b/gfx/skia/skia/src/core/Sk4x4f.h @@ -0,0 +1,153 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef Sk4x4f_DEFINED +#define Sk4x4f_DEFINED + +#include "SkNx.h" + +struct Sk4x4f { + Sk4f r,g,b,a; + + static Sk4x4f Transpose(const Sk4f&, const Sk4f&, const Sk4f&, const Sk4f&); + static Sk4x4f Transpose(const float[16]); + static Sk4x4f Transpose(const uint8_t[16]); + + void transpose(Sk4f* x, Sk4f* y, Sk4f* z, Sk4f* w) const { + auto t = Transpose(r,g,b,a); + *x = t.r; + *y = t.g; + *z = t.b; + *w = t.a; + } + void transpose( float[16]) const; + void transpose(uint8_t[16]) const; +}; + +#if 1 && !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 + +inline Sk4x4f Sk4x4f::Transpose(const Sk4f& x, const Sk4f& y, const Sk4f& z, const Sk4f& w) { + auto r = x.fVec, + g = y.fVec, + b = z.fVec, + a = w.fVec; + _MM_TRANSPOSE4_PS(r,g,b,a); + return { r,g,b,a }; +} + +inline Sk4x4f Sk4x4f::Transpose(const float fs[16]) { + return Transpose(Sk4f::Load(fs+0), Sk4f::Load(fs+4), Sk4f::Load(fs+8), Sk4f::Load(fs+12)); +} + +inline Sk4x4f Sk4x4f::Transpose(const uint8_t bs[16]) { + auto b16 = _mm_loadu_si128((const __m128i*)bs); + + auto mask = _mm_set1_epi32(0xFF); + auto r = _mm_cvtepi32_ps(_mm_and_si128(mask, (b16 ))), + g = _mm_cvtepi32_ps(_mm_and_si128(mask, _mm_srli_epi32(b16, 8))), + b = _mm_cvtepi32_ps(_mm_and_si128(mask, _mm_srli_epi32(b16, 16))), + a = _mm_cvtepi32_ps( _mm_srli_epi32(b16, 24)); + return { r,g,b,a }; +} + +inline void Sk4x4f::transpose(float fs[16]) const { + Sk4f x,y,z,w; + this->transpose(&x,&y,&z,&w); + x.store(fs+ 0); + y.store(fs+ 4); + z.store(fs+ 8); + w.store(fs+12); +} + +inline void Sk4x4f::transpose(uint8_t bs[16]) const { + auto R = _mm_cvttps_epi32(r.fVec), + G = _mm_slli_epi32(_mm_cvttps_epi32(g.fVec), 8), + B = _mm_slli_epi32(_mm_cvttps_epi32(b.fVec), 16), + A = _mm_slli_epi32(_mm_cvttps_epi32(a.fVec), 24); + _mm_storeu_si128((__m128i*)bs, _mm_or_si128(A, _mm_or_si128(B, _mm_or_si128(G, R)))); +} + +#elif defined(SK_ARM_HAS_NEON) + +inline Sk4x4f Sk4x4f::Transpose(const Sk4f& x, const Sk4f& y, const Sk4f& z, const Sk4f& w) { + float32x4x2_t xy = vuzpq_f32(x.fVec, y.fVec), + zw = vuzpq_f32(z.fVec, w.fVec), + rb = vuzpq_f32(xy.val[0], zw.val[0]), + ga = vuzpq_f32(xy.val[1], zw.val[1]); + return { rb.val[0], ga.val[0], rb.val[1], ga.val[1] }; +} + +inline Sk4x4f Sk4x4f::Transpose(const float fs[16]) { + float32x4x4_t v = vld4q_f32(fs); + return { v.val[0], v.val[1], v.val[2], v.val[3] }; +} + +inline Sk4x4f Sk4x4f::Transpose(const uint8_t bs[16]) { + auto b16 = vreinterpretq_u32_u8(vld1q_u8(bs)); + auto r = vcvtq_f32_u32(vandq_u32(vdupq_n_u32(0x000000FF), b16) ), + g = vcvtq_n_f32_u32(vandq_u32(vdupq_n_u32(0x0000FF00), b16), 8), + b = vcvtq_n_f32_u32(vandq_u32(vdupq_n_u32(0x00FF0000), b16), 16), + a = vcvtq_n_f32_u32(vandq_u32(vdupq_n_u32(0xFF000000), b16), 24); + return { r,g,b,a }; +} + +inline void Sk4x4f::transpose(float fs[16]) const { + float32x4x4_t v = {{ r.fVec, g.fVec, b.fVec, a.fVec }}; + vst4q_f32(fs, v); +} + +inline void Sk4x4f::transpose(uint8_t bs[16]) const { + auto R = vandq_u32(vdupq_n_u32(0x000000FF), vcvtq_u32_f32(r.fVec )), + G = vandq_u32(vdupq_n_u32(0x0000FF00), vcvtq_n_u32_f32(g.fVec, 8)), + B = vandq_u32(vdupq_n_u32(0x00FF0000), vcvtq_n_u32_f32(b.fVec, 16)), + A = vandq_u32(vdupq_n_u32(0xFF000000), vcvtq_n_u32_f32(a.fVec, 24)); + vst1q_u8(bs, vreinterpretq_u8_u32(vorrq_u32(A, vorrq_u32(B, vorrq_u32(G, R))))); +} + +#else + +inline Sk4x4f Sk4x4f::Transpose(const Sk4f& x, const Sk4f& y, const Sk4f& z, const Sk4f& w) { + return { + { x[0], y[0], z[0], w[0] }, + { x[1], y[1], z[1], w[1] }, + { x[2], y[2], z[2], w[2] }, + { x[3], y[3], z[3], w[3] }, + }; +} + +inline Sk4x4f Sk4x4f::Transpose(const float fs[16]) { + return Transpose(Sk4f::Load(fs+0), Sk4f::Load(fs+4), Sk4f::Load(fs+8), Sk4f::Load(fs+12)); +} + +inline Sk4x4f Sk4x4f::Transpose(const uint8_t bs[16]) { + return { + { (float)bs[0], (float)bs[4], (float)bs[ 8], (float)bs[12] }, + { (float)bs[1], (float)bs[5], (float)bs[ 9], (float)bs[13] }, + { (float)bs[2], (float)bs[6], (float)bs[10], (float)bs[14] }, + { (float)bs[3], (float)bs[7], (float)bs[11], (float)bs[15] }, + }; +} + +inline void Sk4x4f::transpose(float fs[16]) const { + Sk4f x,y,z,w; + this->transpose(&x,&y,&z,&w); + x.store(fs+ 0); + y.store(fs+ 4); + z.store(fs+ 8); + w.store(fs+12); +} + +inline void Sk4x4f::transpose(uint8_t bs[16]) const { + bs[ 0] = (uint8_t)r[0]; bs[ 1] = (uint8_t)g[0]; bs[ 2] = (uint8_t)b[0]; bs[ 3] = (uint8_t)a[0]; + bs[ 4] = (uint8_t)r[1]; bs[ 5] = (uint8_t)g[1]; bs[ 6] = (uint8_t)b[1]; bs[ 7] = (uint8_t)a[1]; + bs[ 8] = (uint8_t)r[2]; bs[ 9] = (uint8_t)g[2]; bs[10] = (uint8_t)b[2]; bs[11] = (uint8_t)a[2]; + bs[12] = (uint8_t)r[3]; bs[13] = (uint8_t)g[3]; bs[14] = (uint8_t)b[3]; bs[15] = (uint8_t)a[3]; +} + +#endif + +#endif//Sk4x4f_DEFINED diff --git a/gfx/skia/skia/src/core/SkAAClip.cpp b/gfx/skia/skia/src/core/SkAAClip.cpp index 71d212a292..f71d7b61d2 100644 --- a/gfx/skia/skia/src/core/SkAAClip.cpp +++ b/gfx/skia/skia/src/core/SkAAClip.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * diff --git a/gfx/skia/skia/src/core/SkAAClip.h b/gfx/skia/skia/src/core/SkAAClip.h index 0207233386..7b29ef142a 100644 --- a/gfx/skia/skia/src/core/SkAAClip.h +++ b/gfx/skia/skia/src/core/SkAAClip.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * diff --git a/gfx/skia/skia/src/core/SkAdvancedTypefaceMetrics.cpp b/gfx/skia/skia/src/core/SkAdvancedTypefaceMetrics.cpp index 56a557c343..ce2c15b345 100644 --- a/gfx/skia/skia/src/core/SkAdvancedTypefaceMetrics.cpp +++ b/gfx/skia/skia/src/core/SkAdvancedTypefaceMetrics.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -14,11 +13,9 @@ #include #endif -#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) // forward declare structs needed for getAdvanceData() template for freetype struct FT_FaceRec_; typedef struct FT_FaceRec_* FT_Face; -#endif #ifdef SK_BUILD_FOR_MAC #import @@ -68,9 +65,9 @@ void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric* range, range->fAdvance.setCount(0); } -template +template class AutoTDelete> SkAdvancedTypefaceMetrics::AdvanceMetric* appendRange( - SkAutoTDelete >* nextSlot, + AutoTDelete >* nextSlot, int startId) { nextSlot->reset(new SkAdvancedTypefaceMetrics::AdvanceMetric); resetRange(nextSlot->get(), startId); @@ -245,16 +242,23 @@ SkAdvancedTypefaceMetrics::AdvanceMetric* getAdvanceData( if (curRange->fStartId == lastIndex) { SkASSERT(prevRange); SkASSERT(prevRange->fNext->fStartId == lastIndex); - prevRange->fNext.free(); + prevRange->fNext.reset(); } else { finishRange(curRange, lastIndex - 1, SkAdvancedTypefaceMetrics::WidthRange::kRange); } - return result.detach(); + return result.release(); } // Make AdvanceMetric template functions available for linking with typename // WidthRange and VerticalAdvanceRange. +template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( + FT_Face face, + int num_glyphs, + const uint32_t* subsetGlyphIDs, + uint32_t subsetGlyphIDsLength, + bool (*getAdvance)(FT_Face face, int gId, int16_t* data)); + #if defined(SK_BUILD_FOR_WIN) template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( HDC hdc, @@ -268,13 +272,6 @@ template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( const uint32_t* subsetGlyphIDs, uint32_t subsetGlyphIDsLength, bool (*getAdvance)(IDWriteFontFace* fontFace, int gId, int16_t* data)); -#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) -template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( - FT_Face face, - int num_glyphs, - const uint32_t* subsetGlyphIDs, - uint32_t subsetGlyphIDsLength, - bool (*getAdvance)(FT_Face face, int gId, int16_t* data)); #elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData( CTFontRef ctFont, diff --git a/gfx/skia/skia/src/core/SkAdvancedTypefaceMetrics.h b/gfx/skia/skia/src/core/SkAdvancedTypefaceMetrics.h index b2c9ac3142..329b7d13c1 100644 --- a/gfx/skia/skia/src/core/SkAdvancedTypefaceMetrics.h +++ b/gfx/skia/skia/src/core/SkAdvancedTypefaceMetrics.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -16,6 +15,34 @@ #include "SkTDArray.h" #include "SkTemplates.h" +// Whatever std::unique_ptr Clank's using doesn't seem to work with AdvanceMetric's +// style of forward-declaration. Probably just a bug in an old libc++ / libstdc++. +// For now, hack around it with our own smart pointer. It'd be nice to clean up. +template +class SkHackyAutoTDelete : SkNoncopyable { +public: + explicit SkHackyAutoTDelete(T* ptr = nullptr) : fPtr(ptr) {} + ~SkHackyAutoTDelete() { delete fPtr; } + + T* get() const { return fPtr; } + T* operator->() const { return fPtr; } + + void reset(T* ptr = nullptr) { + if (ptr != fPtr) { + delete fPtr; + fPtr = ptr; + } + } + T* release() { + T* ptr = fPtr; + fPtr = nullptr; + return ptr; + } + +private: + T* fPtr; +}; + /** \class SkAdvancedTypefaceMetrics The SkAdvancedTypefaceMetrics class is used by the PDF backend to correctly @@ -97,7 +124,7 @@ public: uint16_t fStartId; uint16_t fEndId; SkTDArray fAdvance; - SkAutoTDelete > fNext; + SkHackyAutoTDelete > fNext; }; struct VerticalMetric { @@ -130,9 +157,9 @@ template void resetRange(SkAdvancedTypefaceMetrics::AdvanceMetric* range, int startId); -template +template class AutoTDelete> SkAdvancedTypefaceMetrics::AdvanceMetric* appendRange( - SkAutoTDelete >* nextSlot, + AutoTDelete >* nextSlot, int startId); template diff --git a/gfx/skia/skia/src/core/SkAlphaRuns.cpp b/gfx/skia/skia/src/core/SkAlphaRuns.cpp index 37b1a5705c..37f57c1fa8 100644 --- a/gfx/skia/skia/src/core/SkAlphaRuns.cpp +++ b/gfx/skia/skia/src/core/SkAlphaRuns.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkAnnotation.cpp b/gfx/skia/skia/src/core/SkAnnotation.cpp index 84d41fc669..09a6296fba 100644 --- a/gfx/skia/skia/src/core/SkAnnotation.cpp +++ b/gfx/skia/skia/src/core/SkAnnotation.cpp @@ -6,38 +6,10 @@ */ #include "SkAnnotation.h" -#include "SkData.h" -#include "SkPaint.h" +#include "SkAnnotationKeys.h" +#include "SkCanvas.h" #include "SkPoint.h" -#include "SkReadBuffer.h" -#include "SkWriteBuffer.h" - -SkAnnotation::SkAnnotation(const char key[], SkData* value) : fKey(key) { - if (nullptr == value) { - value = SkData::NewEmpty(); - } else { - value->ref(); - } - fData = value; -} - -SkAnnotation::~SkAnnotation() { - fData->unref(); -} - -SkData* SkAnnotation::find(const char key[]) const { - return fKey.equals(key) ? fData : nullptr; -} - -SkAnnotation::SkAnnotation(SkReadBuffer& buffer) { - buffer.readString(&fKey); - fData = buffer.readByteArrayAsData(); -} - -void SkAnnotation::writeToBuffer(SkWriteBuffer& buffer) const { - buffer.writeString(fKey.c_str()); - buffer.writeDataAsByteArray(fData); -} +#include "SkRect.h" const char* SkAnnotationKeys::URL_Key() { return "SkAnnotationKey_URL"; @@ -51,37 +23,26 @@ const char* SkAnnotationKeys::Link_Named_Dest_Key() { return "SkAnnotationKey_Link_Named_Dest"; }; -/////////////////////////////////////////////////////////////////////////////// - -#include "SkCanvas.h" - -static void annotate_paint(SkPaint& paint, const char* key, SkData* value) { - paint.setAnnotation(SkAnnotation::Create(key, value))->unref(); -} +////////////////////////////////////////////////////////////////////////////////////////////////// void SkAnnotateRectWithURL(SkCanvas* canvas, const SkRect& rect, SkData* value) { if (nullptr == value) { return; } - SkPaint paint; - annotate_paint(paint, SkAnnotationKeys::URL_Key(), value); - canvas->drawRect(rect, paint); + canvas->drawAnnotation(rect, SkAnnotationKeys::URL_Key(), value); } void SkAnnotateNamedDestination(SkCanvas* canvas, const SkPoint& point, SkData* name) { if (nullptr == name) { return; } - SkPaint paint; - annotate_paint(paint, SkAnnotationKeys::Define_Named_Dest_Key(), name); - canvas->drawPoint(point.x(), point.y(), paint); + const SkRect rect = SkRect::MakeXYWH(point.x(), point.y(), 0, 0); + canvas->drawAnnotation(rect, SkAnnotationKeys::Define_Named_Dest_Key(), name); } void SkAnnotateLinkToDestination(SkCanvas* canvas, const SkRect& rect, SkData* name) { if (nullptr == name) { return; } - SkPaint paint; - annotate_paint(paint, SkAnnotationKeys::Link_Named_Dest_Key(), name); - canvas->drawRect(rect, paint); + canvas->drawAnnotation(rect, SkAnnotationKeys::Link_Named_Dest_Key(), name); } diff --git a/gfx/skia/skia/src/core/SkAnnotationKeys.h b/gfx/skia/skia/src/core/SkAnnotationKeys.h new file mode 100644 index 0000000000..dff9338807 --- /dev/null +++ b/gfx/skia/skia/src/core/SkAnnotationKeys.h @@ -0,0 +1,33 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAnnotationKeys_DEFINED +#define SkAnnotationKeys_DEFINED + +#include "SkTypes.h" + +class SkAnnotationKeys { +public: + /** + * Returns the canonical key whose payload is a URL + */ + static const char* URL_Key(); + + /** + * Returns the canonical key whose payload is the name of a destination to + * be defined. + */ + static const char* Define_Named_Dest_Key(); + + /** + * Returns the canonical key whose payload is the name of a destination to + * be linked to. + */ + static const char* Link_Named_Dest_Key(); +}; + +#endif diff --git a/gfx/skia/skia/src/core/SkAntiRun.h b/gfx/skia/skia/src/core/SkAntiRun.h index d49a723b20..8214e28d5b 100644 --- a/gfx/skia/skia/src/core/SkAntiRun.h +++ b/gfx/skia/skia/src/core/SkAntiRun.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkAutoKern.h b/gfx/skia/skia/src/core/SkAutoKern.h index 0b22e56413..8b032519b3 100644 --- a/gfx/skia/skia/src/core/SkAutoKern.h +++ b/gfx/skia/skia/src/core/SkAutoKern.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -12,11 +11,10 @@ #include "SkGlyph.h" -#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) -#define SkAutoKern_AdjustS(prev, next) SkIntToScalar(((next) - (prev) + 32) >> 6) +#define SkAutoKern_Adjust(prev, next) SkIntToScalar(((next) - (prev) + 32) >> 6) /* this is a helper class to perform auto-kerning - * the adjust() method returns a SkFixed corresponding + * the adjust() method returns a SkScalar corresponding * to a +1/0/-1 pixel adjustment */ @@ -24,7 +22,7 @@ class SkAutoKern { public: SkAutoKern() : fPrevRsbDelta(0) {} - SkFixed adjust(const SkGlyph& glyph) + SkScalar adjust(const SkGlyph& glyph) { // if (SkAbs32(glyph.fLsbDelta) > 47 || SkAbs32(glyph.fRsbDelta) > 47) // printf("------- %d> L %d R %d\n", glyph.f_GlyphID, glyph.fLsbDelta, glyph.fRsbDelta); @@ -35,13 +33,13 @@ public: fPrevRsbDelta = glyph.fRsbDelta; if (distort >= 32) - return -SK_Fixed1; + return -SK_Scalar1; else if (distort < -32) - return +SK_Fixed1; + return +SK_Scalar1; else return 0; #else - SkFixed adjust = SkAutoKern_AdjustF(fPrevRsbDelta, glyph.fLsbDelta); + SkScalar adjust = SkAutoKern_Adjust(fPrevRsbDelta, glyph.fLsbDelta); fPrevRsbDelta = glyph.fRsbDelta; return adjust; #endif diff --git a/gfx/skia/skia/src/core/SkAutoPixmapStorage.cpp b/gfx/skia/skia/src/core/SkAutoPixmapStorage.cpp new file mode 100644 index 0000000000..865b3a6029 --- /dev/null +++ b/gfx/skia/skia/src/core/SkAutoPixmapStorage.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAutoPixmapStorage.h" +#include "SkData.h" + +SkAutoPixmapStorage::SkAutoPixmapStorage() : fStorage(nullptr) {} + +SkAutoPixmapStorage::~SkAutoPixmapStorage() { + this->freeStorage(); +} + +size_t SkAutoPixmapStorage::AllocSize(const SkImageInfo& info, size_t* rowBytes) { + size_t rb = info.minRowBytes(); + if (rowBytes) { + *rowBytes = rb; + } + return info.getSafeSize(rb); +} + +bool SkAutoPixmapStorage::tryAlloc(const SkImageInfo& info) { + this->freeStorage(); + + size_t rb; + size_t size = AllocSize(info, &rb); + if (0 == size) { + return false; + } + void* pixels = sk_malloc_flags(size, 0); + if (nullptr == pixels) { + return false; + } + this->reset(info, pixels, rb); + fStorage = pixels; + return true; +} + +void SkAutoPixmapStorage::alloc(const SkImageInfo& info) { + if (!this->tryAlloc(info)) { + sk_throw(); + } +} + +const SkData* SkAutoPixmapStorage::detachPixelsAsData() { + if (!fStorage) { + return nullptr; + } + + auto data = SkData::MakeFromMalloc(fStorage, this->getSafeSize()); + fStorage = nullptr; + this->INHERITED::reset(); + + return data.release(); +} diff --git a/gfx/skia/skia/src/core/SkAutoPixmapStorage.h b/gfx/skia/skia/src/core/SkAutoPixmapStorage.h new file mode 100644 index 0000000000..379bf420b0 --- /dev/null +++ b/gfx/skia/skia/src/core/SkAutoPixmapStorage.h @@ -0,0 +1,78 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkAutoPixmapStorage_DEFINED +#define SkAutoPixmapStorage_DEFINED + +#include "SkPixmap.h" + +class SK_API SkAutoPixmapStorage : public SkPixmap { +public: + SkAutoPixmapStorage(); + ~SkAutoPixmapStorage(); + + /** + * Try to allocate memory for the pixels needed to match the specified Info. On success + * return true and fill out the pixmap to point to that memory. The storage will be freed + * when this object is destroyed, or if another call to tryAlloc() or alloc() is made. + * + * On failure, return false and reset() the pixmap to empty. + */ + bool tryAlloc(const SkImageInfo&); + + /** + * Allocate memory for the pixels needed to match the specified Info and fill out the pixmap + * to point to that memory. The storage will be freed when this object is destroyed, + * or if another call to tryAlloc() or alloc() is made. + * + * If the memory cannot be allocated, calls sk_throw(). + */ + void alloc(const SkImageInfo&); + + /** + * Gets the size and optionally the rowBytes that would be allocated by SkAutoPixmapStorage if + * alloc/tryAlloc was called. + */ + static size_t AllocSize(const SkImageInfo& info, size_t* rowBytes); + + /** + * Returns an SkData object wrapping the allocated pixels memory, and resets the pixmap. + * If the storage hasn't been allocated, the result is NULL. + */ + const SkData* SK_WARN_UNUSED_RESULT detachPixelsAsData(); + + // We wrap these so we can clear our internal storage + + void reset() { + this->freeStorage(); + this->INHERITED::reset(); + } + void reset(const SkImageInfo& info, const void* addr, size_t rb, SkColorTable* ctable = NULL) { + this->freeStorage(); + this->INHERITED::reset(info, addr, rb, ctable); + } + void reset(const SkImageInfo& info) { + this->freeStorage(); + this->INHERITED::reset(info); + } + bool SK_WARN_UNUSED_RESULT reset(const SkMask& mask) { + this->freeStorage(); + return this->INHERITED::reset(mask); + } + +private: + void* fStorage; + + void freeStorage() { + sk_free(fStorage); + fStorage = nullptr; + } + + typedef SkPixmap INHERITED; +}; + +#endif diff --git a/gfx/skia/skia/src/core/SkBigPicture.cpp b/gfx/skia/skia/src/core/SkBigPicture.cpp index 61f3a0ef1c..70f68db337 100644 --- a/gfx/skia/skia/src/core/SkBigPicture.cpp +++ b/gfx/skia/skia/src/core/SkBigPicture.cpp @@ -88,9 +88,9 @@ SkBigPicture::Analysis::Analysis(const SkRecord& record) { bool hasText = false, hasBitmap = false; for (int i = 0; i < record.count(); i++) { - hasText = hasText || record.visit(i, text); - hasBitmap = hasBitmap || record.visit(i, bitmap); - record.visit(i, path); + hasText = hasText || record.visit(i, text); + hasBitmap = hasBitmap || record.visit(i, bitmap); + record.visit(i, path); } fHasText = hasText; diff --git a/gfx/skia/skia/src/core/SkBigPicture.h b/gfx/skia/skia/src/core/SkBigPicture.h index 2e42213539..0834709f8a 100644 --- a/gfx/skia/skia/src/core/SkBigPicture.h +++ b/gfx/skia/skia/src/core/SkBigPicture.h @@ -10,9 +10,11 @@ #include "SkOncePtr.h" #include "SkPicture.h" +#include "SkRect.h" #include "SkTemplates.h" class SkBBoxHierarchy; +class SkMatrix; class SkRecord; // An implementation of SkPicture supporting an arbitrary number of drawing commands. diff --git a/gfx/skia/skia/src/core/SkBitmap.cpp b/gfx/skia/skia/src/core/SkBitmap.cpp index bdf1daafcc..7e8e62e88b 100644 --- a/gfx/skia/skia/src/core/SkBitmap.cpp +++ b/gfx/skia/skia/src/core/SkBitmap.cpp @@ -563,6 +563,8 @@ void* SkBitmap::getAddr(int x, int y) const { return base; } +#include "SkHalf.h" + SkColor SkBitmap::getColor(int x, int y) const { SkASSERT((unsigned)x < (unsigned)this->width()); SkASSERT((unsigned)y < (unsigned)this->height()); @@ -599,6 +601,18 @@ SkColor SkBitmap::getColor(int x, int y) const { SkPMColor c = SkSwizzle_RGBA_to_PMColor(addr[0]); return SkUnPreMultiply::PMColorToColor(c); } + case kRGBA_F16_SkColorType: { + const uint64_t* addr = (const uint64_t*)fPixels + y * (fRowBytes >> 3) + x; + Sk4f p4 = SkHalfToFloat_01(addr[0]); + if (p4[3]) { + float inva = 1 / p4[3]; + p4 = p4 * Sk4f(inva, inva, inva, 1); + } + SkColor c; + SkNx_cast(p4 * Sk4f(255) + Sk4f(0.5f)).store(&c); + // p4 is RGBA, but we want BGRA, so we need to swap next + return SkSwizzle_RB(c); + } default: SkASSERT(false); return 0; @@ -1149,7 +1163,7 @@ bool SkBitmap::ReadRawPixels(SkReadBuffer* buffer, SkBitmap* bitmap) { return false; } - SkAutoDataUnref data(SkData::NewUninitialized(SkToSizeT(ramSize))); + sk_sp data(SkData::MakeUninitialized(SkToSizeT(ramSize))); unsigned char* dst = (unsigned char*)data->writable_data(); buffer->readByteArray(dst, SkToSizeT(snugSize)); diff --git a/gfx/skia/skia/src/core/SkBitmapController.cpp b/gfx/skia/skia/src/core/SkBitmapController.cpp index 7182ea7382..ac1029db81 100644 --- a/gfx/skia/skia/src/core/SkBitmapController.cpp +++ b/gfx/skia/skia/src/core/SkBitmapController.cpp @@ -49,7 +49,7 @@ public: private: SkBitmap fResultBitmap; SkAutoTUnref fCurrMip; - + bool processHQRequest(const SkBitmapProvider&); bool processMediumRequest(const SkBitmapProvider&); }; @@ -65,7 +65,8 @@ static inline bool cache_size_okay(const SkBitmapProvider& provider, const SkMat // return ((origBitmapSize * matrixScaleFactor) < maximumAllocationSize); // Skip the division step: const size_t size = provider.info().getSafeSize(provider.info().minRowBytes()); - return size < (maximumAllocation * invMat.getScaleX() * invMat.getScaleY()); + SkScalar invScaleSqr = invMat.getScaleX() * invMat.getScaleY(); + return size < (maximumAllocation * SkScalarAbs(invScaleSqr)); } /* @@ -76,17 +77,17 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& pr if (fQuality != kHigh_SkFilterQuality) { return false; } - + // Our default return state is to downgrade the request to Medium, w/ or w/o setting fBitmap // to a valid bitmap. If we succeed, we will set this to Low instead. fQuality = kMedium_SkFilterQuality; - + if (kN32_SkColorType != provider.info().colorType() || !cache_size_okay(provider, fInvMatrix) || fInvMatrix.hasPerspective()) { return false; // can't handle the reqeust } - + SkScalar invScaleX = fInvMatrix.getScaleX(); SkScalar invScaleY = fInvMatrix.getScaleY(); if (fInvMatrix.getType() & SkMatrix::kAffine_Mask) { @@ -97,16 +98,17 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& pr invScaleX = scale.width(); invScaleY = scale.height(); } + invScaleX = SkScalarAbs(invScaleX); + invScaleY = SkScalarAbs(invScaleY); + if (SkScalarNearlyEqual(invScaleX, 1) && SkScalarNearlyEqual(invScaleY, 1)) { return false; // no need for HQ } -#ifndef SK_SUPPORT_LEGACY_HQ_DOWNSAMPLING if (invScaleX > 1 || invScaleY > 1) { return false; // only use HQ when upsampling } -#endif - + const int dstW = SkScalarRoundToScalar(provider.width() / invScaleX); const int dstH = SkScalarRoundToScalar(provider.height() / invScaleY); const SkBitmapCacheDesc desc = provider.makeCacheDesc(dstW, dstH); @@ -124,7 +126,7 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& pr dstW, dstH, SkResourceCache::GetAllocator())) { return false; // we failed to create fScaledBitmap } - + SkASSERT(fResultBitmap.getPixels()); fResultBitmap.setImmutable(); if (!provider.isVolatile()) { @@ -133,9 +135,9 @@ bool SkDefaultBitmapControllerState::processHQRequest(const SkBitmapProvider& pr } } } - + SkASSERT(fResultBitmap.getPixels()); - + fInvMatrix.postScale(SkIntToScalar(dstW) / provider.width(), SkIntToScalar(dstH) / provider.height()); fQuality = kLow_SkFilterQuality; @@ -151,18 +153,17 @@ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider if (fQuality != kMedium_SkFilterQuality) { return false; } - + // Our default return state is to downgrade the request to Low, w/ or w/o setting fBitmap // to a valid bitmap. fQuality = kLow_SkFilterQuality; - + SkSize invScaleSize; if (!fInvMatrix.decomposeScale(&invScaleSize, nullptr)) { return false; } - SkScalar invScale = SkScalarSqrt(invScaleSize.width() * invScaleSize.height()); - - if (invScale > SK_Scalar1) { + + if (invScaleSize.width() > SK_Scalar1 || invScaleSize.height() > SK_Scalar1) { fCurrMip.reset(SkMipMapCache::FindAndRef(provider.makeCacheDesc())); if (nullptr == fCurrMip.get()) { SkBitmap orig; @@ -178,17 +179,17 @@ bool SkDefaultBitmapControllerState::processMediumRequest(const SkBitmapProvider if (nullptr == fCurrMip->data()) { sk_throw(); } - - SkScalar levelScale = SkScalarInvert(invScale); + + const SkSize scale = SkSize::Make(SkScalarInvert(invScaleSize.width()), + SkScalarInvert(invScaleSize.height())); SkMipMap::Level level; - if (fCurrMip->extractLevel(levelScale, &level)) { - SkScalar invScaleFixup = level.fScale; - fInvMatrix.postScale(invScaleFixup, invScaleFixup); - - const SkImageInfo info = provider.info().makeWH(level.fWidth, level.fHeight); + if (fCurrMip->extractLevel(scale, &level)) { + const SkSize& invScaleFixup = level.fScale; + fInvMatrix.postScale(invScaleFixup.width(), invScaleFixup.height()); + // todo: if we could wrap the fCurrMip in a pixelref, then we could just install // that here, and not need to explicitly track it ourselves. - return fResultBitmap.installPixels(info, level.fPixels, level.fRowBytes); + return fResultBitmap.installPixels(level.fPixmap); } else { // failed to extract, so release the mipmap fCurrMip.reset(nullptr); @@ -224,4 +225,3 @@ SkBitmapController::State* SkDefaultBitmapController::onRequestBitmap(const SkBi void* storage, size_t size) { return SkInPlaceNewCheck(storage, size, bm, inverse, quality); } - diff --git a/gfx/skia/skia/src/core/SkBitmapController.h b/gfx/skia/skia/src/core/SkBitmapController.h index 32e73e813d..86b8755ec1 100644 --- a/gfx/skia/skia/src/core/SkBitmapController.h +++ b/gfx/skia/skia/src/core/SkBitmapController.h @@ -27,12 +27,12 @@ public: const SkPixmap& pixmap() const { return fPixmap; } const SkMatrix& invMatrix() const { return fInvMatrix; } SkFilterQuality quality() const { return fQuality; } - + protected: SkPixmap fPixmap; SkMatrix fInvMatrix; SkFilterQuality fQuality; - + private: friend class SkBitmapController; }; @@ -56,7 +56,7 @@ protected: class SkDefaultBitmapController : public SkBitmapController { public: SkDefaultBitmapController() {} - + protected: State* onRequestBitmap(const SkBitmapProvider&, const SkMatrix& inverse, SkFilterQuality, void* storage, size_t storageSize) override; diff --git a/gfx/skia/skia/src/core/SkBitmapDevice.cpp b/gfx/skia/skia/src/core/SkBitmapDevice.cpp index a7ca7b7181..c8ac8d2d44 100644 --- a/gfx/skia/skia/src/core/SkBitmapDevice.cpp +++ b/gfx/skia/skia/src/core/SkBitmapDevice.cpp @@ -20,9 +20,6 @@ class SkColorTable; -#define CHECK_FOR_ANNOTATION(paint) \ - do { if (paint.getAnnotation()) { return; } } while (0) - static bool valid_for_bitmap_device(const SkImageInfo& info, SkAlphaType* newAlphaType) { if (info.width() < 0 || info.height() < 0) { @@ -55,6 +52,8 @@ static bool valid_for_bitmap_device(const SkImageInfo& info, break; case kN32_SkColorType: break; + case kRGBA_F16_SkColorType: + break; default: return false; } @@ -202,18 +201,14 @@ void SkBitmapDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { void SkBitmapDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { - CHECK_FOR_ANNOTATION(paint); draw.drawPoints(mode, count, pts, paint); } void SkBitmapDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) { - CHECK_FOR_ANNOTATION(paint); draw.drawRect(r, paint); } void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) { - CHECK_FOR_ANNOTATION(paint); - SkPath path; path.addOval(oval); // call the VIRTUAL version, so any subclasses who do handle drawPath aren't @@ -222,8 +217,6 @@ void SkBitmapDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPa } void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) { - CHECK_FOR_ANNOTATION(paint); - #ifdef SK_IGNORE_BLURRED_RRECT_OPT SkPath path; @@ -239,7 +232,6 @@ void SkBitmapDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const S void SkBitmapDevice::drawPath(const SkDraw& draw, const SkPath& path, const SkPaint& paint, const SkMatrix* prePathMatrix, bool pathIsMutable) { - CHECK_FOR_ANNOTATION(paint); draw.drawPath(path, paint, prePathMatrix, pathIsMutable); } @@ -322,17 +314,15 @@ void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, } // construct a shader, so we can call drawRect with the dst - SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr, - SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode, - &matrix); - if (nullptr == s) { + auto s = SkShader::MakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, &matrix); + if (!s) { return; } SkPaint paintWithShader(paint); paintWithShader.setStyle(SkPaint::kFill_Style); - paintWithShader.setShader(s)->unref(); + paintWithShader.setShader(std::move(s)); // Call ourself, in case the subclass wanted to share this setup code // but handle the drawRect code themselves. @@ -370,8 +360,8 @@ void SkBitmapDevice::drawDevice(const SkDraw& draw, SkBaseDevice* device, draw.drawSprite(static_cast(device)->fBitmap, x, y, paint); } -SkSurface* SkBitmapDevice::newSurface(const SkImageInfo& info, const SkSurfaceProps& props) { - return SkSurface::NewRaster(info, &props); +sk_sp SkBitmapDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) { + return SkSurface::MakeRaster(info, &props); } SkImageFilter::Cache* SkBitmapDevice::getImageFilterCache() { diff --git a/gfx/skia/skia/src/core/SkBitmapFilter.h b/gfx/skia/skia/src/core/SkBitmapFilter.h index dc3ea693b8..ca3e0930f2 100644 --- a/gfx/skia/skia/src/core/SkBitmapFilter.h +++ b/gfx/skia/skia/src/core/SkBitmapFilter.h @@ -12,9 +12,7 @@ #include "SkMath.h" #include "SkScalar.h" -#ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER #include "SkNx.h" -#endif // size of the precomputed bitmap filter tables for high quality filtering. // Used to precompute the shape of the filter kernel. @@ -30,15 +28,6 @@ public: } virtual ~SkBitmapFilter() {} - SkFixed lookup(float x) const { - if (!fPrecomputed) { - precomputeTable(); - } - int filter_idx = int(sk_float_abs(x * fLookupMultiplier)); - SkASSERT(filter_idx < SKBITMAP_FILTER_TABLE_SIZE); - return fFilterTable[filter_idx]; - } - SkScalar lookupScalar(float x) const { if (!fPrecomputed) { precomputeTable(); @@ -52,7 +41,6 @@ public: float invWidth() const { return fInvWidth; } virtual float evaluate(float x) const = 0; -#ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER virtual float evaluate_n(float val, float diff, int count, float* output) const { float sum = 0; for (int index = 0; index < count; index++) { @@ -63,7 +51,6 @@ public: } return sum; } -#endif protected: float fWidth; @@ -71,28 +58,22 @@ protected: float fLookupMultiplier; mutable bool fPrecomputed; - mutable SkFixed fFilterTable[SKBITMAP_FILTER_TABLE_SIZE]; mutable SkScalar fFilterTableScalar[SKBITMAP_FILTER_TABLE_SIZE]; private: void precomputeTable() const { fPrecomputed = true; - SkFixed *ftp = fFilterTable; SkScalar *ftpScalar = fFilterTableScalar; for (int x = 0; x < SKBITMAP_FILTER_TABLE_SIZE; ++x) { float fx = ((float)x + .5f) * this->width() / SKBITMAP_FILTER_TABLE_SIZE; float filter_value = evaluate(fx); *ftpScalar++ = filter_value; - *ftp++ = SkFloatToFixed(filter_value); } } }; class SkMitchellFilter final : public SkBitmapFilter { public: -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - SkMitchellFilter() : INHERITED(2), B(1.f / 3), C(1.f / 3) {} -#else SkMitchellFilter() : INHERITED(2) , fB(1.f / 3.f) @@ -105,39 +86,20 @@ public: , fB2(-18 + 12*fB + 6*fC) , fD2(6 - 2*fB) {} -#endif float evaluate(float x) const override { x = fabsf(x); if (x > 2.f) { return 0; } else if (x > 1.f) { -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - return ((-B - 6*C) * x*x*x + (6*B + 30*C) * x*x + - (-12*B - 48*C) * x + (8*B + 24*C)) * (1.f/6.f); -#else return (((fA1 * x + fB1) * x + fC1) * x + fD1) * (1.f/6.f); -#endif } else { -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - return ((12 - 9*B - 6*C) * x*x*x + - (-18 + 12*B + 6*C) * x*x + - (6 - 2*B)) * (1.f/6.f); -#else return ((fA2 * x + fB2) * x*x + fD2) * (1.f/6.f); -#endif } } -#ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER - // TODO : native Sk4f abs - static Sk4f abs(const Sk4f& x) { - Sk4f neg = x < Sk4f(0); - return neg.thenElse(Sk4f(0) - x, x); - } - Sk4f evalcore_n(const Sk4f& val) const { - Sk4f x = abs(val); + Sk4f x = val.abs(); Sk4f over2 = x > Sk4f(2); Sk4f over1 = x > Sk4f(1); Sk4f poly1 = (((Sk4f(fA1) * x + Sk4f(fB1)) * x + Sk4f(fC1)) * x + Sk4f(fD1)) @@ -166,16 +128,11 @@ public: result += INHERITED::evaluate_n(val, diff, count, output); return result; } -#endif protected: -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - float B, C; -#else float fB, fC; float fA1, fB1, fC1, fD1; float fA2, fB2, fD2; -#endif private: typedef SkBitmapFilter INHERITED; }; diff --git a/gfx/skia/skia/src/core/SkBitmapHeap.cpp b/gfx/skia/skia/src/core/SkBitmapHeap.cpp index c65a704b5a..071647e13e 100644 --- a/gfx/skia/skia/src/core/SkBitmapHeap.cpp +++ b/gfx/skia/skia/src/core/SkBitmapHeap.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2012 Google Inc. * diff --git a/gfx/skia/skia/src/core/SkBitmapProcShader.cpp b/gfx/skia/skia/src/core/SkBitmapProcShader.cpp index 804c688320..c9df4ce248 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcShader.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcShader.cpp @@ -20,12 +20,239 @@ #include "effects/GrSimpleTextureEffect.h" #endif -size_t SkBitmapProcShader::ContextSize() { - // The SkBitmapProcState is stored outside of the context object, with the context holding - // a pointer to it. - return sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState); +static bool only_scale_and_translate(const SkMatrix& matrix) { + unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; + return (matrix.getType() & ~mask) == 0; } +class BitmapProcInfoContext : public SkShader::Context { +public: + // The info has been allocated elsewhere, but we are responsible for calling its destructor. + BitmapProcInfoContext(const SkShader& shader, const SkShader::ContextRec& rec, + SkBitmapProcInfo* info) + : INHERITED(shader, rec) + , fInfo(info) + { + fFlags = 0; + if (fInfo->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) { + fFlags |= SkShader::kOpaqueAlpha_Flag; + } + + if (1 == fInfo->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) { + fFlags |= SkShader::kConstInY32_Flag; + } + } + + ~BitmapProcInfoContext() override { + fInfo->~SkBitmapProcInfo(); + } + + uint32_t getFlags() const override { return fFlags; } + +private: + SkBitmapProcInfo* fInfo; + uint32_t fFlags; + + typedef SkShader::Context INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +class BitmapProcShaderContext : public BitmapProcInfoContext { +public: + BitmapProcShaderContext(const SkShader& shader, const SkShader::ContextRec& rec, + SkBitmapProcState* state) + : INHERITED(shader, rec, state) + , fState(state) + {} + + void shadeSpan(int x, int y, SkPMColor dstC[], int count) override { + const SkBitmapProcState& state = *fState; + if (state.getShaderProc32()) { + state.getShaderProc32()(&state, x, y, dstC, count); + return; + } + + const int BUF_MAX = 128; + uint32_t buffer[BUF_MAX]; + SkBitmapProcState::MatrixProc mproc = state.getMatrixProc(); + SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32(); + const int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX); + + SkASSERT(state.fPixmap.addr()); + + for (;;) { + int n = SkTMin(count, max); + SkASSERT(n > 0 && n < BUF_MAX*2); + mproc(state, buffer, n, x, y); + sproc(state, buffer, n, dstC); + + if ((count -= n) == 0) { + break; + } + SkASSERT(count > 0); + x += n; + dstC += n; + } + } + + ShadeProc asAShadeProc(void** ctx) override { + if (fState->getShaderProc32()) { + *ctx = fState; + return (ShadeProc)fState->getShaderProc32(); + } + return nullptr; + } + +private: + SkBitmapProcState* fState; + + typedef BitmapProcInfoContext INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkLinearBitmapPipeline.h" +#include "SkPM4f.h" +#include "SkXfermode.h" + +class LinearPipelineContext : public BitmapProcInfoContext { +public: + LinearPipelineContext(const SkShader& shader, const SkShader::ContextRec& rec, + SkBitmapProcInfo* info) + : INHERITED(shader, rec, info) + { + // Need to ensure that our pipeline is created at a 16byte aligned address + fPipeline = (SkLinearBitmapPipeline*)SkAlign16((intptr_t)fStorage); + float alpha = SkColorGetA(info->fPaintColor) / 255.0f; + new (fPipeline) SkLinearBitmapPipeline(info->fRealInvMatrix, info->fFilterQuality, + info->fTileModeX, info->fTileModeY, + alpha, + info->fPixmap); + + // To implement the old shadeSpan entry-point, we need to efficiently convert our native + // floats into SkPMColor. The SkXfermode::D32Procs do exactly that. + // + sk_sp xfer(SkXfermode::Make(SkXfermode::kSrc_Mode)); + fXferProc = SkXfermode::GetD32Proc(xfer.get(), 0); + } + + ~LinearPipelineContext() override { + // since we did a manual new, we need to manually destroy as well. + fPipeline->~SkLinearBitmapPipeline(); + } + + void shadeSpan4f(int x, int y, SkPM4f dstC[], int count) override { + fPipeline->shadeSpan4f(x, y, dstC, count); + } + + void shadeSpan(int x, int y, SkPMColor dstC[], int count) override { + const int N = 128; + SkPM4f tmp[N]; + + while (count > 0) { + const int n = SkTMin(count, N); + fPipeline->shadeSpan4f(x, y, tmp, n); + fXferProc(nullptr, dstC, tmp, n, nullptr); + dstC += n; + x += n; + count -= n; + } + } + +private: + enum { + kActualSize = sizeof(SkLinearBitmapPipeline), + kPaddedSize = SkAlignPtr(kActualSize + 12), + }; + void* fStorage[kPaddedSize / sizeof(void*)]; + SkLinearBitmapPipeline* fPipeline; + SkXfermode::D32Proc fXferProc; + + typedef BitmapProcInfoContext INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool choose_linear_pipeline(const SkShader::ContextRec& rec, const SkImageInfo& srcInfo) { + // These src attributes are not supported in the new 4f context (yet) + // + if (srcInfo.colorType() != kRGBA_8888_SkColorType + && srcInfo.colorType() != kBGRA_8888_SkColorType + && srcInfo.colorType() != kIndex_8_SkColorType) { + return false; + } + +#if 0 // later we may opt-in to the new code even if the client hasn't requested it... + // These src attributes are only supported in the new 4f context + // + if (srcInfo.isSRGB() || + kUnpremul_SkAlphaType == srcInfo.alphaType() || + (4 == srcInfo.bytesPerPixel() && kN32_SkColorType != srcInfo.colorType())) + { + return true; + } +#endif + + // If we get here, we can reasonably use either context, respect the caller's preference + // + return SkShader::ContextRec::kPM4f_DstType == rec.fPreferredDstType; +} + +size_t SkBitmapProcShader::ContextSize(const ContextRec& rec, const SkImageInfo& srcInfo) { + size_t size0 = sizeof(BitmapProcShaderContext) + sizeof(SkBitmapProcState); + size_t size1 = sizeof(LinearPipelineContext) + sizeof(SkBitmapProcInfo); + return SkTMax(size0, size1); +} + +SkShader::Context* SkBitmapProcShader::MakeContext(const SkShader& shader, + TileMode tmx, TileMode tmy, + const SkBitmapProvider& provider, + const ContextRec& rec, void* storage) { + SkMatrix totalInverse; + // Do this first, so we know the matrix can be inverted. + if (!shader.computeTotalInverse(rec, &totalInverse)) { + return nullptr; + } + + // Decide if we can/want to use the new linear pipeline + bool useLinearPipeline = choose_linear_pipeline(rec, provider.info()); + + // + // For now, only enable locally since we are hitting some crashers on the test bots + // + //useLinearPipeline = false; + + if (useLinearPipeline) { + void* infoStorage = (char*)storage + sizeof(LinearPipelineContext); + SkBitmapProcInfo* info = new (infoStorage) SkBitmapProcInfo(provider, tmx, tmy); + if (!info->init(totalInverse, *rec.fPaint)) { + info->~SkBitmapProcInfo(); + return nullptr; + } + if (info->fPixmap.colorType() != kRGBA_8888_SkColorType + && info->fPixmap.colorType() != kBGRA_8888_SkColorType + && info->fPixmap.colorType() != kIndex_8_SkColorType) { + return nullptr; + } + return new (storage) LinearPipelineContext(shader, rec, info); + } else { + void* stateStorage = (char*)storage + sizeof(BitmapProcShaderContext); + SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy); + if (!state->setup(totalInverse, *rec.fPaint)) { + state->~SkBitmapProcState(); + return nullptr; + } + return new (storage) BitmapProcShaderContext(shader, rec, state); + } +} + +SkShader::Context* SkBitmapProcShader::onCreateContext(const ContextRec& rec, void* storage) const { + return MakeContext(*this, (TileMode)fTileModeX, (TileMode)fTileModeY, + SkBitmapProvider(fRawBitmap), rec, storage); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + SkBitmapProcShader::SkBitmapProcShader(const SkBitmap& src, TileMode tmx, TileMode tmy, const SkMatrix* localMatrix) : INHERITED(localMatrix) { @@ -48,7 +275,7 @@ bool SkBitmapProcShader::onIsABitmap(SkBitmap* texture, SkMatrix* texM, TileMode return true; } -SkFlattenable* SkBitmapProcShader::CreateProc(SkReadBuffer& buffer) { +sk_sp SkBitmapProcShader::CreateProc(SkReadBuffer& buffer) { SkMatrix lm; buffer.readMatrix(&lm); SkBitmap bm; @@ -58,7 +285,7 @@ SkFlattenable* SkBitmapProcShader::CreateProc(SkReadBuffer& buffer) { bm.setImmutable(); TileMode mx = (TileMode)buffer.readUInt(); TileMode my = (TileMode)buffer.readUInt(); - return SkShader::CreateBitmapShader(bm, mx, my, &lm); + return SkShader::MakeBitmapShader(bm, mx, my, &lm); } void SkBitmapProcShader::flatten(SkWriteBuffer& buffer) const { @@ -72,123 +299,7 @@ bool SkBitmapProcShader::isOpaque() const { return fRawBitmap.isOpaque(); } -SkShader::Context* SkBitmapProcShader::MakeContext(const SkShader& shader, - TileMode tmx, TileMode tmy, - const SkBitmapProvider& provider, - const ContextRec& rec, void* storage) { - SkMatrix totalInverse; - // Do this first, so we know the matrix can be inverted. - if (!shader.computeTotalInverse(rec, &totalInverse)) { - return nullptr; - } - - void* stateStorage = (char*)storage + sizeof(BitmapProcShaderContext); - SkBitmapProcState* state = new (stateStorage) SkBitmapProcState(provider, tmx, tmy); - - SkASSERT(state); - if (!state->chooseProcs(totalInverse, *rec.fPaint)) { - state->~SkBitmapProcState(); - return nullptr; - } - - return new (storage) BitmapProcShaderContext(shader, rec, state); -} - -SkShader::Context* SkBitmapProcShader::onCreateContext(const ContextRec& rec, void* storage) const { - return MakeContext(*this, (TileMode)fTileModeX, (TileMode)fTileModeY, - SkBitmapProvider(fRawBitmap), rec, storage); -} - -static bool only_scale_and_translate(const SkMatrix& matrix) { - unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; - return (matrix.getType() & ~mask) == 0; -} - -SkBitmapProcShader::BitmapProcShaderContext::BitmapProcShaderContext(const SkShader& shader, - const ContextRec& rec, - SkBitmapProcState* state) - : INHERITED(shader, rec) - , fState(state) -{ - fFlags = 0; - if (fState->fPixmap.isOpaque() && (255 == this->getPaintAlpha())) { - fFlags |= kOpaqueAlpha_Flag; - } - - if (1 == fState->fPixmap.height() && only_scale_and_translate(this->getTotalInverse())) { - fFlags |= kConstInY32_Flag; - } -} - -SkBitmapProcShader::BitmapProcShaderContext::~BitmapProcShaderContext() { - // The bitmap proc state has been created outside of the context on memory that will be freed - // elsewhere. Only call the destructor but leave the freeing of the memory to the caller. - fState->~SkBitmapProcState(); -} - -#define BUF_MAX 128 - -#define TEST_BUFFER_OVERRITEx - -#ifdef TEST_BUFFER_OVERRITE - #define TEST_BUFFER_EXTRA 32 - #define TEST_PATTERN 0x88888888 -#else - #define TEST_BUFFER_EXTRA 0 -#endif - -void SkBitmapProcShader::BitmapProcShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], - int count) { - const SkBitmapProcState& state = *fState; - if (state.getShaderProc32()) { - state.getShaderProc32()(&state, x, y, dstC, count); - return; - } - - uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA]; - SkBitmapProcState::MatrixProc mproc = state.getMatrixProc(); - SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32(); - int max = state.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX); - - SkASSERT(state.fPixmap.addr()); - - for (;;) { - int n = count; - if (n > max) { - n = max; - } - SkASSERT(n > 0 && n < BUF_MAX*2); -#ifdef TEST_BUFFER_OVERRITE - for (int i = 0; i < TEST_BUFFER_EXTRA; i++) { - buffer[BUF_MAX + i] = TEST_PATTERN; - } -#endif - mproc(state, buffer, n, x, y); -#ifdef TEST_BUFFER_OVERRITE - for (int j = 0; j < TEST_BUFFER_EXTRA; j++) { - SkASSERT(buffer[BUF_MAX + j] == TEST_PATTERN); - } -#endif - sproc(state, buffer, n, dstC); - - if ((count -= n) == 0) { - break; - } - SkASSERT(count > 0); - x += n; - dstC += n; - } -} - -SkShader::Context::ShadeProc SkBitmapProcShader::BitmapProcShaderContext::asAShadeProc(void** ctx) { - if (fState->getShaderProc32()) { - *ctx = fState; - return (ShadeProc)fState->getShaderProc32(); - } - return nullptr; -} - -/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// #include "SkUnPreMultiply.h" #include "SkColorShader.h" @@ -237,9 +348,9 @@ static bool bitmap_is_too_big(const SkBitmap& bm) { return bm.width() > kMaxSize || bm.height() > kMaxSize; } -SkShader* SkCreateBitmapShader(const SkBitmap& src, SkShader::TileMode tmx, - SkShader::TileMode tmy, const SkMatrix* localMatrix, - SkTBlitterAllocator* allocator) { +sk_sp SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode tmx, + SkShader::TileMode tmy, const SkMatrix* localMatrix, + SkTBlitterAllocator* allocator) { SkShader* shader; SkColor color; if (src.isNull() || bitmap_is_too_big(src)) { @@ -261,7 +372,7 @@ SkShader* SkCreateBitmapShader(const SkBitmap& src, SkShader::TileMode tmx, shader = allocator->createT(src, tmx, tmy, localMatrix); } } - return shader; + return sk_sp(shader); } /////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/core/SkBitmapProcShader.h b/gfx/skia/skia/src/core/SkBitmapProcShader.h index 0346eff0e2..185a95de66 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcShader.h +++ b/gfx/skia/skia/src/core/SkBitmapProcShader.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,7 +5,6 @@ * found in the LICENSE file. */ - #ifndef SkBitmapProcShader_DEFINED #define SkBitmapProcShader_DEFINED @@ -23,8 +21,6 @@ public: bool isOpaque() const override; - size_t contextSize() const override { return ContextSize(); } - SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapProcShader) @@ -34,36 +30,20 @@ public: #endif protected: - class BitmapProcShaderContext : public SkShader::Context { - public: - // The context takes ownership of the state. It will call its destructor - // but will NOT free the memory. - BitmapProcShaderContext(const SkShader&, const ContextRec&, SkBitmapProcState*); - ~BitmapProcShaderContext() override; - - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; - ShadeProc asAShadeProc(void** ctx) override; - - uint32_t getFlags() const override { return fFlags; } - - private: - SkBitmapProcState* fState; - uint32_t fFlags; - - typedef SkShader::Context INHERITED; - }; - void flatten(SkWriteBuffer&) const override; + size_t onContextSize(const ContextRec& rec) const override { + return ContextSize(rec, fRawBitmap.info()); + } Context* onCreateContext(const ContextRec&, void* storage) const override; bool onIsABitmap(SkBitmap*, SkMatrix*, TileMode*) const override; - SkBitmap fRawBitmap; // experimental for RLE encoding + SkBitmap fRawBitmap; uint8_t fTileModeX, fTileModeY; private: friend class SkImageShader; - static size_t ContextSize(); + static size_t ContextSize(const ContextRec&, const SkImageInfo& srcInfo); static Context* MakeContext(const SkShader&, TileMode tmx, TileMode tmy, const SkBitmapProvider&, const ContextRec&, void* storage); @@ -75,11 +55,11 @@ private: // an Sk3DBlitter in SkDraw.cpp // Note that some contexts may contain other contexts (e.g. for compose shaders), but we've not // yet found a situation where the size below isn't big enough. -typedef SkSmallAllocator<3, 1160> SkTBlitterAllocator; +typedef SkSmallAllocator<3, 2100> SkTBlitterAllocator; // If alloc is non-nullptr, it will be used to allocate the returned SkShader, and MUST outlive // the SkShader. -SkShader* SkCreateBitmapShader(const SkBitmap& src, SkShader::TileMode, SkShader::TileMode, - const SkMatrix* localMatrix, SkTBlitterAllocator* alloc); +sk_sp SkMakeBitmapShader(const SkBitmap& src, SkShader::TileMode, SkShader::TileMode, + const SkMatrix* localMatrix, SkTBlitterAllocator* alloc); #endif diff --git a/gfx/skia/skia/src/core/SkBitmapProcState.cpp b/gfx/skia/skia/src/core/SkBitmapProcState.cpp index b1438cb8a0..fc749116df 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcState.cpp @@ -36,25 +36,23 @@ extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void*, int, int, u #include "SkBitmapProcState_filter.h" #include "SkBitmapProcState_procs.h" -SkBitmapProcState::SkBitmapProcState(const SkBitmapProvider& provider, - SkShader::TileMode tmx, SkShader::TileMode tmy) +SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmapProvider& provider, + SkShader::TileMode tmx, SkShader::TileMode tmy) : fProvider(provider) + , fTileModeX(tmx) + , fTileModeY(tmy) , fBMState(nullptr) -{ - fTileModeX = tmx; - fTileModeY = tmy; -} +{} -SkBitmapProcState::SkBitmapProcState(const SkBitmap& bm, - SkShader::TileMode tmx, SkShader::TileMode tmy) +SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmap& bm, + SkShader::TileMode tmx, SkShader::TileMode tmy) : fProvider(SkBitmapProvider(bm)) + , fTileModeX(tmx) + , fTileModeY(tmy) , fBMState(nullptr) -{ - fTileModeX = tmx; - fTileModeY = tmy; -} +{} -SkBitmapProcState::~SkBitmapProcState() { +SkBitmapProcInfo::~SkBitmapProcInfo() { SkInPlaceDeleteCheck(fBMState, fBMStateStorage.get()); } @@ -118,20 +116,18 @@ static bool valid_for_filtering(unsigned dimension) { return (dimension & ~0x3FFF) == 0; } -/* - * Analyze filter-quality and matrix, and decide how to implement that. - * - * In general, we cascade down the request level [ High ... None ] - * - for a given level, if we can fulfill it, fine, else - * - else we downgrade to the next lower level and try again. - * We can always fulfill requests for Low and None - * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack - * and may be removed. - */ -bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { +bool SkBitmapProcInfo::init(const SkMatrix& inv, const SkPaint& paint) { + const int origW = fProvider.info().width(); + const int origH = fProvider.info().height(); + fPixmap.reset(); fInvMatrix = inv; - fFilterLevel = paint.getFilterQuality(); + fFilterQuality = paint.getFilterQuality(); + + bool allow_ignore_fractional_translate = true; // historical default + if (kMedium_SkFilterQuality == fFilterQuality) { + allow_ignore_fractional_translate = false; + } SkDefaultBitmapController controller; fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(), @@ -142,9 +138,11 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { } fPixmap = fBMState->pixmap(); fInvMatrix = fBMState->invMatrix(); - fFilterLevel = fBMState->quality(); + fRealInvMatrix = fBMState->invMatrix(); + fPaintColor = paint.getColor(); + fFilterQuality = fBMState->quality(); SkASSERT(fPixmap.addr()); - + bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY; @@ -171,49 +169,71 @@ bool SkBitmapProcState::chooseProcs(const SkMatrix& inv, const SkPaint& paint) { if (matrix_only_scale_translate(fInvMatrix)) { SkMatrix forward; if (fInvMatrix.invert(&forward)) { - if (clampClamp ? just_trans_clamp(forward, fPixmap) - : just_trans_general(forward)) { - SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX()); - SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY()); - fInvMatrix.setTranslate(tx, ty); + if ((clampClamp && allow_ignore_fractional_translate) + ? just_trans_clamp(forward, fPixmap) + : just_trans_general(forward)) { + fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY()); } } } - fInvProc = fInvMatrix.getMapXYProc(); - fInvType = fInvMatrix.getType(); - fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); - fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); - fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); - fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); + fInvType = fInvMatrix.getType(); - fAlphaScale = SkAlpha255To256(paint.getAlpha()); + // If our target pixmap is the same as the original, then we revert back to legacy behavior + // and allow the code to ignore fractional translate. + // + // The width/height check allows allow_ignore_fractional_translate to stay false if we + // previously set it that way (e.g. we started in kMedium). + // + if (fPixmap.width() == origW && fPixmap.height() == origH) { + allow_ignore_fractional_translate = true; + } - fShaderProc32 = nullptr; - fShaderProc16 = nullptr; - fSampleProc32 = nullptr; - - // recompute the triviality of the matrix here because we may have - // changed it! - - trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; - - if (kLow_SkFilterQuality == fFilterLevel) { + if (kLow_SkFilterQuality == fFilterQuality && allow_ignore_fractional_translate) { // Only try bilerp if the matrix is "interesting" and // the image has a suitable size. if (fInvType <= SkMatrix::kTranslate_Mask || !valid_for_filtering(fPixmap.width() | fPixmap.height())) { - fFilterLevel = kNone_SkFilterQuality; + fFilterQuality = kNone_SkFilterQuality; } } - return this->chooseScanlineProcs(trivialMatrix, clampClamp, paint); + return true; } -bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp, - const SkPaint& paint) { +/* + * Analyze filter-quality and matrix, and decide how to implement that. + * + * In general, we cascade down the request level [ High ... None ] + * - for a given level, if we can fulfill it, fine, else + * - else we downgrade to the next lower level and try again. + * We can always fulfill requests for Low and None + * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack + * and may be removed. + */ +bool SkBitmapProcState::chooseProcs() { + fInvProc = fInvMatrix.getMapXYProc(); + fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); + fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); + fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); + fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); + + fAlphaScale = SkAlpha255To256(SkColorGetA(fPaintColor)); + + fShaderProc32 = nullptr; + fShaderProc16 = nullptr; + fSampleProc32 = nullptr; + + const bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; + const bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && + SkShader::kClamp_TileMode == fTileModeY; + + return this->chooseScanlineProcs(trivialMatrix, clampClamp); +} + +bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp) { fMatrixProc = this->chooseMatrixProc(trivialMatrix); // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns nullptr. if (nullptr == fMatrixProc) { @@ -228,7 +248,7 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp, // still set to HQ by the time we get here, then we must have installed // the shader procs above and can skip all this. - if (fFilterLevel < kHigh_SkFilterQuality) { + if (fFilterQuality < kHigh_SkFilterQuality) { int index = 0; if (fAlphaScale < 256) { // note: this distinction is not used for D16 @@ -237,7 +257,7 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp, if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { index |= 2; } - if (fFilterLevel > kNone_SkFilterQuality) { + if (fFilterQuality > kNone_SkFilterQuality) { index |= 4; } // bits 3,4,5 encoding the source bitmap format @@ -265,11 +285,11 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp, break; case kAlpha_8_SkColorType: index |= 32; - fPaintPMColor = SkPreMultiplyColor(paint.getColor()); + fPaintPMColor = SkPreMultiplyColor(fPaintColor); break; case kGray_8_SkColorType: index |= 40; - fPaintPMColor = SkPreMultiplyColor(paint.getColor()); + fPaintPMColor = SkPreMultiplyColor(fPaintColor); break; default: // TODO(dominikg): Should we ever get here? SkASSERT(false) instead? @@ -313,7 +333,7 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp, S4444_alpha_D32_filter_DXDY, S4444_opaque_D32_filter_DX, S4444_alpha_D32_filter_DX, - + // A8 treats alpha/opaque the same (equally efficient) SA8_alpha_D32_nofilter_DXDY, SA8_alpha_D32_nofilter_DXDY, @@ -323,7 +343,7 @@ bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp, SA8_alpha_D32_filter_DXDY, SA8_alpha_D32_filter_DX, SA8_alpha_D32_filter_DX, - + // todo: possibly specialize on opaqueness SG8_alpha_D32_nofilter_DXDY, SG8_alpha_D32_nofilter_DXDY, @@ -365,24 +385,12 @@ static void Clamp_S32_D32_nofilter_trans_shaderproc(const void* sIn, SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0); SkASSERT(s.fInvKy == 0); SkASSERT(count > 0 && colors != nullptr); - SkASSERT(kNone_SkFilterQuality == s.fFilterLevel); + SkASSERT(kNone_SkFilterQuality == s.fFilterQuality); const int maxX = s.fPixmap.width() - 1; const int maxY = s.fPixmap.height() - 1; int ix = s.fFilterOneX + x; int iy = SkClampMax(s.fFilterOneY + y, maxY); -#ifdef SK_DEBUG - { - SkPoint pt; - s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &pt); - int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY); - int ix2 = SkScalarFloorToInt(pt.fX); - - SkASSERT(iy == iy2); - SkASSERT(ix == ix2); - } -#endif const SkPMColor* row = s.fPixmap.addr32(0, iy); // clamp to the left @@ -440,24 +448,12 @@ static void Repeat_S32_D32_nofilter_trans_shaderproc(const void* sIn, SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0); SkASSERT(s.fInvKy == 0); SkASSERT(count > 0 && colors != nullptr); - SkASSERT(kNone_SkFilterQuality == s.fFilterLevel); + SkASSERT(kNone_SkFilterQuality == s.fFilterQuality); const int stopX = s.fPixmap.width(); const int stopY = s.fPixmap.height(); int ix = s.fFilterOneX + x; int iy = sk_int_mod(s.fFilterOneY + y, stopY); -#ifdef SK_DEBUG - { - SkPoint pt; - s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &pt); - int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY); - int ix2 = SkScalarFloorToInt(pt.fX); - - SkASSERT(iy == iy2); - SkASSERT(ix == ix2); - } -#endif const SkPMColor* row = s.fPixmap.addr32(0, iy); ix = sk_int_mod(ix, stopX); @@ -487,7 +483,7 @@ static void S32_D32_constX_shaderproc(const void* sIn, int iY1 SK_INIT_TO_AVOID_WARNING; int iSubY SK_INIT_TO_AVOID_WARNING; - if (kNone_SkFilterQuality != s.fFilterLevel) { + if (kNone_SkFilterQuality != s.fFilterQuality) { SkBitmapProcState::MatrixProc mproc = s.getMatrixProc(); uint32_t xy[2]; @@ -500,20 +496,17 @@ static void S32_D32_constX_shaderproc(const void* sIn, int yTemp; if (s.fInvType > SkMatrix::kTranslate_Mask) { - SkPoint pt; - s.fInvProc(s.fInvMatrix, - SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, - &pt); + const SkBitmapProcStateAutoMapper mapper(s, x, y); + // When the matrix has a scale component the setup code in // chooseProcs multiples the inverse matrix by the inverse of the // bitmap's width and height. Since this method is going to do // its own tiling and sampling we need to undo that here. if (SkShader::kClamp_TileMode != s.fTileModeX || SkShader::kClamp_TileMode != s.fTileModeY) { - yTemp = SkScalarFloorToInt(pt.fY * s.fPixmap.height()); + yTemp = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height()); } else { - yTemp = SkScalarFloorToInt(pt.fY); + yTemp = mapper.intY(); } } else { yTemp = s.fFilterOneY + y; @@ -535,28 +528,27 @@ static void S32_D32_constX_shaderproc(const void* sIn, #ifdef SK_DEBUG { - SkPoint pt; - s.fInvProc(s.fInvMatrix, - SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, - &pt); + const SkBitmapProcStateAutoMapper mapper(s, x, y); + int iY2; + if (s.fInvType > SkMatrix::kTranslate_Mask && (SkShader::kClamp_TileMode != s.fTileModeX || SkShader::kClamp_TileMode != s.fTileModeY)) { - pt.fY *= s.fPixmap.height(); + iY2 = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height()); + } else { + iY2 = mapper.intY(); } - int iY2; switch (s.fTileModeY) { case SkShader::kClamp_TileMode: - iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1); + iY2 = SkClampMax(iY2, stopY-1); break; case SkShader::kRepeat_TileMode: - iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY); + iY2 = sk_int_mod(iY2, stopY); break; case SkShader::kMirror_TileMode: default: - iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY); + iY2 = sk_int_mirror(iY2, stopY); break; } @@ -568,7 +560,7 @@ static void S32_D32_constX_shaderproc(const void* sIn, const SkPMColor* row0 = s.fPixmap.addr32(0, iY0); SkPMColor color; - if (kNone_SkFilterQuality != s.fFilterLevel) { + if (kNone_SkFilterQuality != s.fFilterQuality) { const SkPMColor* row1 = s.fPixmap.addr32(0, iY1); if (s.fAlphaScale < 256) { @@ -595,7 +587,7 @@ static void DoNothing_shaderproc(const void*, int x, int y, bool SkBitmapProcState::setupForTranslate() { SkPoint pt; - fInvProc(fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt); + const SkBitmapProcStateAutoMapper mapper(*this, 0, 0, &pt); /* * if the translate is larger than our ints, we can get random results, or @@ -610,8 +602,9 @@ bool SkBitmapProcState::setupForTranslate() { // Since we know we're not filtered, we re-purpose these fields allow // us to go from device -> src coordinates w/ just an integer add, // rather than running through the inverse-matrix - fFilterOneX = SkScalarFloorToInt(pt.fX); - fFilterOneY = SkScalarFloorToInt(pt.fY); + fFilterOneX = mapper.intX(); + fFilterOneY = mapper.intY(); + return true; } @@ -624,7 +617,7 @@ SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() { static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; if (1 == fPixmap.width() && 0 == (fInvType & ~kMask)) { - if (kNone_SkFilterQuality == fFilterLevel && + if (kNone_SkFilterQuality == fFilterQuality && fInvType <= SkMatrix::kTranslate_Mask && !this->setupForTranslate()) { return DoNothing_shaderproc; @@ -638,7 +631,7 @@ SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() { if (fInvType > SkMatrix::kTranslate_Mask) { return nullptr; } - if (kNone_SkFilterQuality != fFilterLevel) { + if (kNone_SkFilterQuality != fFilterQuality) { return nullptr; } @@ -734,9 +727,11 @@ void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state, // scale -vs- affine // filter -vs- nofilter if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { - proc = state.fFilterLevel != kNone_SkFilterQuality ? check_scale_filter : check_scale_nofilter; + proc = state.fFilterQuality != kNone_SkFilterQuality ? + check_scale_filter : check_scale_nofilter; } else { - proc = state.fFilterLevel != kNone_SkFilterQuality ? check_affine_filter : check_affine_nofilter; + proc = state.fFilterQuality != kNone_SkFilterQuality ? + check_affine_filter : check_affine_nofilter; } proc(bitmapXY, count, state.fPixmap.width(), state.fPixmap.height()); } @@ -771,7 +766,7 @@ int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const { size >>= 2; } - if (fFilterLevel != kNone_SkFilterQuality) { + if (fFilterQuality != kNone_SkFilterQuality) { size >>= 1; } @@ -792,8 +787,8 @@ void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y, { const SkBitmapProcStateAutoMapper mapper(s, x, y); const unsigned maxY = s.fPixmap.height() - 1; - dstY = SkClampMax(SkFractionalIntToInt(mapper.y()), maxY); - fx = mapper.x(); + dstY = SkClampMax(mapper.intY(), maxY); + fx = mapper.fractionalIntX(); } const SkPMColor* SK_RESTRICT src = s.fPixmap.addr32(0, dstY); @@ -829,4 +824,3 @@ void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y, } } } - diff --git a/gfx/skia/skia/src/core/SkBitmapProcState.h b/gfx/skia/skia/src/core/SkBitmapProcState.h index 3f1d699cf2..40dc31a5e0 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState.h @@ -27,10 +27,42 @@ typedef SkFixed3232 SkFractionalInt; class SkPaint; -struct SkBitmapProcState { - SkBitmapProcState(const SkBitmapProvider&, SkShader::TileMode tmx, SkShader::TileMode tmy); - SkBitmapProcState(const SkBitmap&, SkShader::TileMode tmx, SkShader::TileMode tmy); - ~SkBitmapProcState(); +struct SkBitmapProcInfo { + SkBitmapProcInfo(const SkBitmapProvider&, SkShader::TileMode tmx, SkShader::TileMode tmy); + SkBitmapProcInfo(const SkBitmap&, SkShader::TileMode tmx, SkShader::TileMode tmy); + ~SkBitmapProcInfo(); + + const SkBitmapProvider fProvider; + + SkPixmap fPixmap; + SkMatrix fInvMatrix; // This changes based on tile mode. + // TODO: combine fInvMatrix and fRealInvMatrix. + SkMatrix fRealInvMatrix; // The actual inverse matrix. + SkColor fPaintColor; + SkShader::TileMode fTileModeX; + SkShader::TileMode fTileModeY; + SkFilterQuality fFilterQuality; + SkMatrix::TypeMask fInvType; + + bool init(const SkMatrix& inverse, const SkPaint&); + +private: + enum { + kBMStateSize = 136 // found by inspection. if too small, we will call new/delete + }; + SkAlignedSStorage fBMStateStorage; + SkBitmapController::State* fBMState; +}; + +struct SkBitmapProcState : public SkBitmapProcInfo { + SkBitmapProcState(const SkBitmapProvider& prov, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapProcInfo(prov, tmx, tmy) {} + SkBitmapProcState(const SkBitmap& bitmap, SkShader::TileMode tmx, SkShader::TileMode tmy) + : SkBitmapProcInfo(bitmap, tmx, tmy) {} + + bool setup(const SkMatrix& inv, const SkPaint& paint) { + return this->init(inv, paint) && this->chooseProcs(); + } typedef void (*ShaderProc32)(const void* ctx, int x, int y, SkPMColor[], int count); @@ -50,11 +82,7 @@ struct SkBitmapProcState { typedef U16CPU (*FixedTileLowBitsProc)(SkFixed, int); // returns 0..0xF typedef U16CPU (*IntTileProc)(int value, int count); // returns 0..count-1 - SkPixmap fPixmap; - SkMatrix fInvMatrix; // copy of what is in fBMState, can we remove the dup? - SkMatrix::MapXYProc fInvProc; // chooseProcs - SkFractionalInt fInvSxFractionalInt; SkFractionalInt fInvKyFractionalInt; @@ -66,14 +94,10 @@ struct SkBitmapProcState { SkFixed fFilterOneX; SkFixed fFilterOneY; - SkPMColor fPaintPMColor; // chooseProcs - A8 config SkFixed fInvSx; // chooseProcs SkFixed fInvKy; // chooseProcs + SkPMColor fPaintPMColor; // chooseProcs - A8 config uint16_t fAlphaScale; // chooseProcs - uint8_t fInvType; // chooseProcs - uint8_t fTileModeX; // CONSTRUCTOR - uint8_t fTileModeY; // CONSTRUCTOR - uint8_t fFilterLevel; // chooseProcs /** Platforms implement this, and can optionally overwrite only the following fields: @@ -114,26 +138,15 @@ struct SkBitmapProcState { SampleProc32 getSampleProc32() const { return fSampleProc32; } private: - friend class SkBitmapProcShader; - friend class SkLightingShaderImpl; - ShaderProc32 fShaderProc32; // chooseProcs ShaderProc16 fShaderProc16; // chooseProcs // These are used if the shaderproc is nullptr MatrixProc fMatrixProc; // chooseProcs SampleProc32 fSampleProc32; // chooseProcs - const SkBitmapProvider fProvider; - - enum { - kBMStateSize = 136 // found by inspection. if too small, we will call new/delete - }; - SkAlignedSStorage fBMStateStorage; - SkBitmapController::State* fBMState; - MatrixProc chooseMatrixProc(bool trivial_matrix); - bool chooseProcs(const SkMatrix& inv, const SkPaint&); - bool chooseScanlineProcs(bool trivialMatrix, bool clampClamp, const SkPaint& paint); + bool chooseProcs(); // caller must have called init() first (on our base-class) + bool chooseScanlineProcs(bool trivialMatrix, bool clampClamp); ShaderProc32 chooseShaderProc32(); // Return false if we failed to setup for fast translate (e.g. overflow) @@ -189,27 +202,60 @@ void ClampX_ClampY_nofilter_affine(const SkBitmapProcState& s, uint32_t xy[], int count, int x, int y); // Helper class for mapping the middle of pixel (x, y) into SkFractionalInt bitmap space. -// TODO: filtered version which applies a fFilterOne{X,Y}/2 bias instead of epsilon? +// Discussion: +// Overall, this code takes a point in destination space, and uses the center of the pixel +// at (x, y) to determine the sample point in source space. It then adjusts the pixel by different +// amounts based in filtering and tiling. +// This code can be broken into two main cases based on filtering: +// * no filtering (nearest neighbor) - when using nearest neighbor filtering all tile modes reduce +// the sampled by one ulp. If a simple point pt lies precisely on XXX.1/2 then it forced down +// when positive making 1/2 + 1/2 = .999999 instead of 1.0. +// * filtering - in the filtering case, the code calculates the -1/2 shift for starting the +// bilerp kernel. There is a twist; there is a big difference between clamp and the other tile +// modes. In tile and repeat the matrix has been reduced by an additional 1/width and 1/height +// factor. This maps from destination space to [0, 1) (instead of source space) to allow easy +// modulo arithmetic. This means that the -1/2 needed by bilerp is actually 1/2 * 1/width for x +// and 1/2 * 1/height for y. This is what happens when the poorly named fFilterOne{X|Y} is +// divided by two. class SkBitmapProcStateAutoMapper { public: - SkBitmapProcStateAutoMapper(const SkBitmapProcState& s, int x, int y) { + SkBitmapProcStateAutoMapper(const SkBitmapProcState& s, int x, int y, + SkPoint* scalarPoint = nullptr) { SkPoint pt; s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, SkIntToScalar(y) + SK_ScalarHalf, &pt); - // SkFixed epsilon bias to ensure inverse-mapped bitmap coordinates are rounded - // consistently WRT geometry. Note that we only need the bias for positive scales: - // for negative scales, the rounding is intrinsically correct. - // We scale it to persist SkFractionalInt -> SkFixed conversions. - const SkFixed biasX = (s.fInvMatrix.getScaleX() > 0); - const SkFixed biasY = (s.fInvMatrix.getScaleY() > 0); + SkFixed biasX, biasY; + if (s.fFilterQuality == kNone_SkFilterQuality) { + // SkFixed epsilon bias to ensure inverse-mapped bitmap coordinates are rounded + // consistently WRT geometry. Note that we only need the bias for positive scales: + // for negative scales, the rounding is intrinsically correct. + // We scale it to persist SkFractionalInt -> SkFixed conversions. + biasX = (s.fInvMatrix.getScaleX() > 0); + biasY = (s.fInvMatrix.getScaleY() > 0); + } else { + biasX = s.fFilterOneX >> 1; + biasY = s.fFilterOneY >> 1; + } + fX = SkScalarToFractionalInt(pt.x()) - SkFixedToFractionalInt(biasX); fY = SkScalarToFractionalInt(pt.y()) - SkFixedToFractionalInt(biasY); + + if (scalarPoint) { + scalarPoint->set(pt.x() - SkFixedToScalar(biasX), + pt.y() - SkFixedToScalar(biasY)); + } } - SkFractionalInt x() const { return fX; } - SkFractionalInt y() const { return fY; } + SkFractionalInt fractionalIntX() const { return fX; } + SkFractionalInt fractionalIntY() const { return fY; } + + SkFixed fixedX() const { return SkFractionalIntToFixed(fX); } + SkFixed fixedY() const { return SkFractionalIntToFixed(fY); } + + int intX() const { return SkFractionalIntToInt(fX); } + int intY() const { return SkFractionalIntToInt(fY); } private: SkFractionalInt fX, fY; diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_filter.h b/gfx/skia/skia/src/core/SkBitmapProcState_filter.h index 99f40eca56..dfc18d8a2b 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_filter.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_filter.h @@ -1,4 +1,3 @@ - /* * Copyright 2009 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_matrix.h b/gfx/skia/skia/src/core/SkBitmapProcState_matrix.h index bdab846496..7e2e44bcee 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_matrix.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_matrix.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -61,15 +60,13 @@ void SCALE_FILTER_NAME(const SkBitmapProcState& s, SkFractionalInt fx; { - SkPoint pt; - s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &pt); - const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1); + const SkBitmapProcStateAutoMapper mapper(s, x, y); + const SkFixed fy = mapper.fixedY(); const unsigned maxY = s.fPixmap.height() - 1; // compute our two Y values up front *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y); // now initialize fx - fx = SkScalarToFractionalInt(pt.fX) - (SkFixedToFractionalInt(one) >> 1); + fx = mapper.fractionalIntX(); } #ifdef CHECK_FOR_DECAL @@ -95,15 +92,12 @@ void AFFINE_FILTER_NAME(const SkBitmapProcState& s, SkMatrix::kAffine_Mask)) == 0); PREAMBLE(s); - SkPoint srcPt; - s.fInvProc(s.fInvMatrix, - SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &srcPt); + const SkBitmapProcStateAutoMapper mapper(s, x, y); SkFixed oneX = s.fFilterOneX; SkFixed oneY = s.fFilterOneY; - SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1); - SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1); + SkFixed fx = mapper.fixedX(); + SkFixed fy = mapper.fixedY(); SkFixed dx = s.fInvSx; SkFixed dy = s.fInvKy; unsigned maxX = s.fPixmap.width() - 1; diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp b/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp index 1c4b7b6bcf..16f1bc6f28 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProcState_matrixProcs.cpp @@ -171,22 +171,12 @@ static inline U16CPU fixed_repeat(SkFixed x) { return x & 0xFFFF; } -// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly. -// See http://code.google.com/p/skia/issues/detail?id=472 -#if defined(_MSC_VER) && (_MSC_VER >= 1600) -#pragma optimize("", off) -#endif - static inline U16CPU fixed_mirror(SkFixed x) { SkFixed s = SkLeftShift(x, 15) >> 31; // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval return (x ^ s) & 0xFFFF; } -#if defined(_MSC_VER) && (_MSC_VER >= 1600) -#pragma optimize("", on) -#endif - static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m) { if (SkShader::kClamp_TileMode == m) { return fixed_clamp; @@ -328,14 +318,11 @@ static void fill_sequential(uint16_t xptr[], int start, int count) { static int nofilter_trans_preamble(const SkBitmapProcState& s, uint32_t** xy, int x, int y) { - SkPoint pt; - s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &pt); - **xy = s.fIntTileProcY(SkScalarToFixed(pt.fY) >> 16, - s.fPixmap.height()); + const SkBitmapProcStateAutoMapper mapper(s, x, y); + **xy = s.fIntTileProcY(mapper.intY(), s.fPixmap.height()); *xy += 1; // bump the ptr // return our starting X position - return SkScalarToFixed(pt.fX) >> 16; + return mapper.intX(); } static void clampx_nofilter_trans(const SkBitmapProcState& s, @@ -488,7 +475,7 @@ static void mirrorx_nofilter_trans(const SkBitmapProcState& s, SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool trivial_matrix) { // test_int_tileprocs(); // check for our special case when there is no scale/affine/perspective - if (trivial_matrix && kNone_SkFilterQuality == fFilterLevel) { + if (trivial_matrix && kNone_SkFilterQuality == fFilterQuality) { fIntTileProcY = choose_int_tile_proc(fTileModeY); switch (fTileModeX) { case SkShader::kClamp_TileMode: @@ -501,7 +488,7 @@ SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool trivial_m } int index = 0; - if (fFilterLevel != kNone_SkFilterQuality) { + if (fFilterQuality != kNone_SkFilterQuality) { index = 1; } if (fInvType & SkMatrix::kPerspective_Mask) { diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_matrix_template.h b/gfx/skia/skia/src/core/SkBitmapProcState_matrix_template.h index 82fdddbece..0c9371851c 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_matrix_template.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_matrix_template.h @@ -24,8 +24,8 @@ void NoFilterProc_Scale(const SkBitmapProcState& s, uint32_t xy[], { const SkBitmapProcStateAutoMapper mapper(s, x, y); const unsigned maxY = s.fPixmap.height() - 1; - *xy++ = TileProc::Y(s, SkFractionalIntToFixed(mapper.y()), maxY); - fx = mapper.x(); + *xy++ = TileProc::Y(s, mapper.fixedY(), maxY); + fx = mapper.fractionalIntX(); } if (0 == maxX) { @@ -79,8 +79,8 @@ void NoFilterProc_Affine(const SkBitmapProcState& s, uint32_t xy[], const SkBitmapProcStateAutoMapper mapper(s, x, y); - SkFractionalInt fx = mapper.x(); - SkFractionalInt fy = mapper.y(); + SkFractionalInt fx = mapper.fractionalIntX(); + SkFractionalInt fy = mapper.fractionalIntY(); SkFractionalInt dx = s.fInvSxFractionalInt; SkFractionalInt dy = s.fInvKyFractionalInt; int maxX = s.fPixmap.width() - 1; diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_sample.h b/gfx/skia/skia/src/core/SkBitmapProcState_sample.h index 4a022884d5..8526c47cdb 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_sample.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_sample.h @@ -25,7 +25,7 @@ void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, int count, SkPMColor* SK_RESTRICT colors) { SkASSERT(count > 0 && colors != nullptr); - SkASSERT(kNone_SkFilterQuality == s.fFilterLevel); + SkASSERT(kNone_SkFilterQuality == s.fFilterQuality); SkDEBUGCODE(CHECKSTATE(s);) #ifdef PREAMBLE @@ -68,7 +68,7 @@ void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s, int count, SkPMColor* SK_RESTRICT colors) { SkASSERT(count > 0 && colors != nullptr); SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)); - SkASSERT(kNone_SkFilterQuality == s.fFilterLevel); + SkASSERT(kNone_SkFilterQuality == s.fFilterQuality); SkDEBUGCODE(CHECKSTATE(s);) #ifdef PREAMBLE @@ -122,7 +122,7 @@ void MAKENAME(_filter_DX)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, int count, SkPMColor* SK_RESTRICT colors) { SkASSERT(count > 0 && colors != nullptr); - SkASSERT(s.fFilterLevel != kNone_SkFilterQuality); + SkASSERT(s.fFilterQuality != kNone_SkFilterQuality); SkDEBUGCODE(CHECKSTATE(s);) #ifdef PREAMBLE @@ -168,7 +168,7 @@ void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s, const uint32_t* SK_RESTRICT xy, int count, SkPMColor* SK_RESTRICT colors) { SkASSERT(count > 0 && colors != nullptr); - SkASSERT(s.fFilterLevel != kNone_SkFilterQuality); + SkASSERT(s.fFilterQuality != kNone_SkFilterQuality); SkDEBUGCODE(CHECKSTATE(s);) #ifdef PREAMBLE diff --git a/gfx/skia/skia/src/core/SkBitmapProcState_shaderproc.h b/gfx/skia/skia/src/core/SkBitmapProcState_shaderproc.h index d41ff063e4..523b5621e2 100644 --- a/gfx/skia/skia/src/core/SkBitmapProcState_shaderproc.h +++ b/gfx/skia/skia/src/core/SkBitmapProcState_shaderproc.h @@ -19,7 +19,7 @@ void SCALE_FILTER_NAME(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT col SkMatrix::kScale_Mask)) == 0); SkASSERT(s.fInvKy == 0); SkASSERT(count > 0 && colors != nullptr); - SkASSERT(s.fFilterLevel != kNone_SkFilterQuality); + SkASSERT(s.fFilterQuality != kNone_SkFilterQuality); SkDEBUGCODE(CHECKSTATE(s);) const unsigned maxX = s.fPixmap.width() - 1; @@ -31,10 +31,8 @@ void SCALE_FILTER_NAME(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT col unsigned subY; { - SkPoint pt; - s.fInvProc(s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf, - SkIntToScalar(y) + SK_ScalarHalf, &pt); - SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1); + const SkBitmapProcStateAutoMapper mapper(s, x, y); + SkFixed fy = mapper.fixedY(); const unsigned maxY = s.fPixmap.height() - 1; // compute our two Y values up front subY = TILEY_LOW_BITS(fy, maxY); @@ -46,7 +44,7 @@ void SCALE_FILTER_NAME(const void* sIn, int x, int y, SkPMColor* SK_RESTRICT col row0 = (const SRCTYPE*)(srcAddr + y0 * rb); row1 = (const SRCTYPE*)(srcAddr + y1 * rb); // now initialize fx - fx = SkScalarToFixed(pt.fX) - (oneX >> 1); + fx = mapper.fixedX(); } #ifdef PREAMBLE diff --git a/gfx/skia/skia/src/core/SkBitmapProvider.cpp b/gfx/skia/skia/src/core/SkBitmapProvider.cpp index 9cf2e9ae6b..9f6eb94310 100644 --- a/gfx/skia/skia/src/core/SkBitmapProvider.cpp +++ b/gfx/skia/skia/src/core/SkBitmapProvider.cpp @@ -84,4 +84,3 @@ bool SkBitmapProvider::asBitmap(SkBitmap* bm) const { return true; } } - diff --git a/gfx/skia/skia/src/core/SkBitmapScaler.cpp b/gfx/skia/skia/src/core/SkBitmapScaler.cpp index dc04691c79..398e20c246 100644 --- a/gfx/skia/skia/src/core/SkBitmapScaler.cpp +++ b/gfx/skia/skia/src/core/SkBitmapScaler.cpp @@ -144,42 +144,20 @@ void SkResizeFilter::computeFilters(int srcSize, // 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). -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - int destLimit = SkScalarTruncToInt(SkScalarCeilToScalar(destSubsetHi) - - SkScalarFloorToScalar(destSubsetLo)); -#else destSubsetLo = SkScalarFloorToScalar(destSubsetLo); destSubsetHi = SkScalarCeilToScalar(destSubsetHi); float srcPixel = (destSubsetLo + 0.5f) * invScale; int destLimit = SkScalarTruncToInt(destSubsetHi - destSubsetLo); -#endif output->reserveAdditional(destLimit, SkScalarCeilToInt(destLimit * srcSupport * 2)); -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - for (int destSubsetI = SkScalarFloorToInt(destSubsetLo); destSubsetI < SkScalarCeilToInt(destSubsetHi); - destSubsetI++) -#else for (int destI = 0; destI < destLimit; srcPixel += invScale, destI++) -#endif { // Compute the (inclusive) range of source pixels the filter covers. -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - float srcPixel = (static_cast(destSubsetI) + 0.5f) * invScale; - int srcBegin = SkTMax(0, SkScalarFloorToInt(srcPixel - srcSupport)); - int srcEnd = SkTMin(srcSize - 1, SkScalarCeilToInt(srcPixel + srcSupport)); -#else float srcBegin = SkTMax(0.f, SkScalarFloorToScalar(srcPixel - srcSupport)); float srcEnd = SkTMin(srcSize - 1.f, SkScalarCeilToScalar(srcPixel + srcSupport)); -#endif // Compute the unnormalized filter value at each location of the source // it covers. -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - float filterSum = 0.0f; // Sub of the filter values for normalizing. - int filterCount = srcEnd - srcBegin + 1; - filterValuesArray.reset(filterCount); - for (int curFilterPixel = srcBegin; curFilterPixel <= srcEnd; - curFilterPixel++) { -#endif + // Sum of the filter values for normalizing. // 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 @@ -187,42 +165,25 @@ void SkResizeFilter::computeFilters(int srcSize, // 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). -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - float srcFilterDist = - ((static_cast(curFilterPixel) + 0.5f) - srcPixel); - - // Since the filter really exists in dest space, map it there. - float destFilterDist = srcFilterDist * clampedScale; - - // Compute the filter value at that location. - float filterValue = fBitmapFilter->evaluate(destFilterDist); - filterValuesArray[curFilterPixel - srcBegin] = filterValue; - - filterSum += filterValue; - } -#else float destFilterDist = (srcBegin + 0.5f - srcPixel) * clampedScale; int filterCount = SkScalarTruncToInt(srcEnd - srcBegin) + 1; - SkASSERT(filterCount > 0); + if (filterCount <= 0) { + // true when srcSize is equal to srcPixel - srcSupport; this may be a bug + return; + } filterValuesArray.reset(filterCount); float filterSum = fBitmapFilter->evaluate_n(destFilterDist, clampedScale, filterCount, filterValuesArray.begin()); -#endif + // The filter must be normalized so that we don't affect the brightness of // the image. Convert to normalized fixed point. int fixedSum = 0; fixedFilterValuesArray.reset(filterCount); const float* filterValues = filterValuesArray.begin(); SkConvolutionFilter1D::ConvolutionFixed* fixedFilterValues = fixedFilterValuesArray.begin(); -#ifndef SK_SUPPORT_LEGACY_BITMAP_FILTER float invFilterSum = 1 / filterSum; -#endif for (int fixedI = 0; fixedI < filterCount; fixedI++) { -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] / filterSum); -#else int curFixed = SkConvolutionFilter1D::FloatToFixed(filterValues[fixedI] * invFilterSum); -#endif fixedSum += curFixed; fixedFilterValues[fixedI] = SkToS16(curFixed); } @@ -237,11 +198,7 @@ void SkResizeFilter::computeFilters(int srcSize, fixedFilterValues[filterCount / 2] += leftovers; // Now it's ready to go. -#ifdef SK_SUPPORT_LEGACY_BITMAP_FILTER - output->AddFilter(srcBegin, fixedFilterValues, filterCount); -#else output->AddFilter(SkScalarFloorToInt(srcBegin), fixedFilterValues, filterCount); -#endif } if (convolveProcs.fApplySIMDPadding) { @@ -306,4 +263,3 @@ bool SkBitmapScaler::Resize(SkBitmap* resultPtr, const SkPixmap& source, ResizeM SkASSERT(resultPtr->getPixels()); return true; } - diff --git a/gfx/skia/skia/src/core/SkBlitBWMaskTemplate.h b/gfx/skia/skia/src/core/SkBlitBWMaskTemplate.h index accf498aba..b0b9358b49 100644 --- a/gfx/skia/skia/src/core/SkBlitBWMaskTemplate.h +++ b/gfx/skia/skia/src/core/SkBlitBWMaskTemplate.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkBlitRow_D32.cpp b/gfx/skia/skia/src/core/SkBlitRow_D32.cpp index 75330ae337..80c7242104 100644 --- a/gfx/skia/skia/src/core/SkBlitRow_D32.cpp +++ b/gfx/skia/skia/src/core/SkBlitRow_D32.cpp @@ -52,35 +52,6 @@ static void S32_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst, } } -static void S32A_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst, - const SkPMColor* SK_RESTRICT src, - int count, U8CPU alpha) { - SkASSERT(255 == alpha); - if (count > 0) { -#ifdef UNROLL - if (count & 1) { - *dst = SkPMSrcOver(*(src++), *dst); - dst += 1; - count -= 1; - } - - const SkPMColor* SK_RESTRICT srcEnd = src + count; - while (src != srcEnd) { - *dst = SkPMSrcOver(*(src++), *dst); - dst += 1; - *dst = SkPMSrcOver(*(src++), *dst); - dst += 1; - } -#else - do { - *dst = SkPMSrcOver(*src, *dst); - src += 1; - dst += 1; - } while (--count > 0); -#endif - } -} - static void S32A_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst, const SkPMColor* SK_RESTRICT src, int count, U8CPU alpha) { @@ -115,7 +86,7 @@ static void S32A_Blend_BlitRow32(SkPMColor* SK_RESTRICT dst, static const SkBlitRow::Proc32 gDefault_Procs32[] = { S32_Opaque_BlitRow32, S32_Blend_BlitRow32, - S32A_Opaque_BlitRow32, + nullptr, S32A_Blend_BlitRow32 }; @@ -124,6 +95,11 @@ SkBlitRow::Proc32 SkBlitRow::Factory32(unsigned flags) { // just so we don't crash flags &= kFlags32_Mask; + if (flags == 2) { + // S32A_Opaque_BlitRow32 has been ported to SkOpts, but not the others yet. + return SkOpts::blit_row_s32a_opaque; + } + SkBlitRow::Proc32 proc = PlatformProcs32(flags); if (nullptr == proc) { proc = gDefault_Procs32[flags]; diff --git a/gfx/skia/skia/src/core/SkBlitter.cpp b/gfx/skia/skia/src/core/SkBlitter.cpp index b73be40a45..8f0031cd46 100644 --- a/gfx/skia/skia/src/core/SkBlitter.cpp +++ b/gfx/skia/skia/src/core/SkBlitter.cpp @@ -19,6 +19,9 @@ #include "SkXfermode.h" #include "SkXfermodeInterpretation.h" +// define this for testing srgb blits +//#define SK_FORCE_PM4f_FOR_L32_BLITS + SkBlitter::~SkBlitter() {} bool SkBlitter::isNullBlitter() const { return false; } @@ -124,6 +127,10 @@ static uint8_t generate_right_mask(int maskBitCount) { void SkBlitter::blitMask(const SkMask& mask, const SkIRect& clip) { SkASSERT(mask.fBounds.contains(clip)); + if (mask.fFormat == SkMask::kLCD16_Format) { + return; // needs to be handled by subclass + } + if (mask.fFormat == SkMask::kBW_Format) { int cx = clip.fLeft; int cy = clip.fTop; @@ -577,18 +584,12 @@ SkBlitter* SkBlitterClipper::apply(SkBlitter* blitter, const SkRegion* clip, class Sk3DShader : public SkShader { public: - Sk3DShader(SkShader* proxy) : fProxy(proxy) { - SkSafeRef(proxy); - } + Sk3DShader(sk_sp proxy) : fProxy(std::move(proxy)) {} - virtual ~Sk3DShader() { - SkSafeUnref(fProxy); - } - - size_t contextSize() const override { + size_t onContextSize(const ContextRec& rec) const override { size_t size = sizeof(Sk3DShaderContext); if (fProxy) { - size += fProxy->contextSize(); + size += fProxy->contextSize(rec); } return size; } @@ -720,18 +721,17 @@ public: protected: void flatten(SkWriteBuffer& buffer) const override { - buffer.writeFlattenable(fProxy); + buffer.writeFlattenable(fProxy.get()); } private: - SkShader* fProxy; + sk_sp fProxy; typedef SkShader INHERITED; }; -SkFlattenable* Sk3DShader::CreateProc(SkReadBuffer& buffer) { - SkAutoTUnref shader(buffer.readShader()); - return new Sk3DShader(shader); +sk_sp Sk3DShader::CreateProc(SkReadBuffer& buffer) { + return sk_make_sp(buffer.readShader()); } class Sk3DBlitter : public SkBlitter { @@ -745,8 +745,7 @@ public: fProxy->blitH(x, y, width); } - virtual void blitAntiH(int x, int y, const SkAlpha antialias[], - const int16_t runs[]) override { + void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override { fProxy->blitAntiH(x, y, antialias, runs); } @@ -782,6 +781,16 @@ private: #include "SkCoreBlitters.h" +SkShader::ContextRec::DstType SkBlitter::PreferredShaderDest(const SkImageInfo& dstInfo) { +#ifdef SK_FORCE_PM4f_FOR_L32_BLITS + return SkShader::ContextRec::kPM4f_DstType; +#else + return (dstInfo.isSRGB() || dstInfo.colorType() == kRGBA_F16_SkColorType) + ? SkShader::ContextRec::kPM4f_DstType + : SkShader::ContextRec::kPMColor_DstType; +#endif +} + SkBlitter* SkBlitter::Choose(const SkPixmap& device, const SkMatrix& matrix, const SkPaint& origPaint, @@ -799,16 +808,16 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, SkShader* shader = origPaint.getShader(); SkColorFilter* cf = origPaint.getColorFilter(); SkXfermode* mode = origPaint.getXfermode(); - Sk3DShader* shader3D = nullptr; + sk_sp shader3D; SkTCopyOnFirstWrite paint(origPaint); if (origPaint.getMaskFilter() != nullptr && origPaint.getMaskFilter()->getFormat() == SkMask::k3D_Format) { - shader3D = new Sk3DShader(shader); + shader3D = sk_make_sp(sk_ref_sp(shader)); // we know we haven't initialized lazyPaint yet, so just do it - paint.writable()->setShader(shader3D)->unref(); - shader = shader3D; + paint.writable()->setShader(shader3D); + shader = shader3D.get(); } if (mode) { @@ -833,8 +842,10 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, */ if (SkXfermode::IsMode(mode, SkXfermode::kClear_Mode)) { SkPaint* p = paint.writable(); - shader = p->setShader(nullptr); - cf = p->setColorFilter(nullptr); + p->setShader(nullptr); + shader = nullptr; + p->setColorFilter(nullptr); + cf = nullptr; mode = p->setXfermodeMode(SkXfermode::kSrc_Mode); p->setColor(0); } @@ -842,9 +853,9 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, if (nullptr == shader) { if (mode) { // xfermodes (and filters) require shaders for our current blitters - shader = new SkColorShader(paint->getColor()); - paint.writable()->setShader(shader)->unref(); + paint.writable()->setShader(SkShader::MakeColorShader(paint->getColor())); paint.writable()->setAlpha(0xFF); + shader = paint->getShader(); } else if (cf) { // if no shader && no xfermode, we just apply the colorfilter to // our color and move on. @@ -857,8 +868,8 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, if (cf) { SkASSERT(shader); - shader = shader->newWithColorFilter(cf); - paint.writable()->setShader(shader)->unref(); + paint.writable()->setShader(shader->makeWithColorFilter(sk_ref_sp(cf))); + shader = paint->getShader(); // blitters should ignore the presence/absence of a filter, since // if there is one, the shader will take care of it. } @@ -868,8 +879,9 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, */ SkShader::Context* shaderContext = nullptr; if (shader) { - SkShader::ContextRec rec(*paint, matrix, nullptr); - size_t contextSize = shader->contextSize(); + const SkShader::ContextRec rec(*paint, matrix, nullptr, + PreferredShaderDest(device.info())); + size_t contextSize = shader->contextSize(rec); if (contextSize) { // Try to create the ShaderContext void* storage = allocator->reserveT(contextSize); @@ -904,22 +916,38 @@ SkBlitter* SkBlitter::Choose(const SkPixmap& device, break; case kN32_SkColorType: - if (shader) { - blitter = allocator->createT( - device, *paint, shaderContext); - } else if (paint->getColor() == SK_ColorBLACK) { - blitter = allocator->createT(device, *paint); - } else if (paint->getAlpha() == 0xFF) { - blitter = allocator->createT(device, *paint); +#ifdef SK_FORCE_PM4f_FOR_L32_BLITS + if (true) +#else + if (device.info().isSRGB()) +#endif + { + blitter = SkBlitter_ARGB32_Create(device, *paint, shaderContext, allocator); } else { - blitter = allocator->createT(device, *paint); + if (shader) { + blitter = allocator->createT( + device, *paint, shaderContext); + } else if (paint->getColor() == SK_ColorBLACK) { + blitter = allocator->createT(device, *paint); + } else if (paint->getAlpha() == 0xFF) { + blitter = allocator->createT(device, *paint); + } else { + blitter = allocator->createT(device, *paint); + } } break; - default: - SkDEBUGFAIL("unsupported device config"); - blitter = allocator->createT(); + case kRGBA_F16_SkColorType: + // kU16_SkColorType: + blitter = SkBlitter_ARGB64_Create(device, *paint, shaderContext, allocator); break; + + default: + break; + } + + if (!blitter) { + blitter = allocator->createT(); } if (shader3D) { @@ -938,7 +966,8 @@ class SkZeroShaderContext : public SkShader::Context { public: SkZeroShaderContext(const SkShader& shader, const SkShader::ContextRec& rec) // Override rec with the identity matrix, so it is guaranteed to be invertible. - : INHERITED(shader, SkShader::ContextRec(*rec.fPaint, SkMatrix::I(), nullptr)) {} + : INHERITED(shader, SkShader::ContextRec(*rec.fPaint, SkMatrix::I(), nullptr, + rec.fPreferredDstType)) {} void shadeSpan(int x, int y, SkPMColor colors[], int count) override { sk_bzero(colors, count * sizeof(SkPMColor)); @@ -958,6 +987,7 @@ SkShaderBlitter::SkShaderBlitter(const SkPixmap& device, const SkPaint& paint, fShader->ref(); fShaderFlags = fShaderContext->getFlags(); + fConstInY = SkToBool(fShaderFlags & SkShader::kConstInY32_Flag); } SkShaderBlitter::~SkShaderBlitter() { diff --git a/gfx/skia/skia/src/core/SkBlitter.h b/gfx/skia/skia/src/core/SkBlitter.h index 5740a5eaed..0095826248 100644 --- a/gfx/skia/skia/src/core/SkBlitter.h +++ b/gfx/skia/skia/src/core/SkBlitter.h @@ -59,7 +59,7 @@ public: virtual void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) { int16_t runs[3]; uint8_t aa[2]; - + runs[0] = 1; runs[1] = 1; runs[2] = 0; @@ -72,7 +72,7 @@ public: virtual void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) { int16_t runs[2]; uint8_t aa[1]; - + runs[0] = 1; runs[1] = 0; aa[0] = SkToU8(a0); @@ -83,7 +83,7 @@ public: aa[0] = SkToU8(a1); this->blitAntiH(x, y + 1, aa, runs); } - + /** * Special method just to identify the null blitter, which is returned * from Choose() if the request cannot be fulfilled. Default impl @@ -137,6 +137,8 @@ public: SkTBlitterAllocator*); ///@} + static SkShader::ContextRec::DstType PreferredShaderDest(const SkImageInfo&); + protected: SkAutoMalloc fBlitMemory; }; diff --git a/gfx/skia/skia/src/core/SkBlitter_A8.cpp b/gfx/skia/skia/src/core/SkBlitter_A8.cpp index 51748b00e9..57587c675a 100644 --- a/gfx/skia/skia/src/core/SkBlitter_A8.cpp +++ b/gfx/skia/skia/src/core/SkBlitter_A8.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp b/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp index a00ed86d8e..af62f2eb1d 100644 --- a/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp +++ b/gfx/skia/skia/src/core/SkBlitter_ARGB32.cpp @@ -62,7 +62,7 @@ const SkPixmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) { return nullptr; } -#if defined _WIN32 && _MSC_VER >= 1300 // disable warning : local variable used without having been initialized +#if defined _WIN32 // disable warning : local variable used without having been initialized #pragma warning ( push ) #pragma warning ( disable : 4701 ) #endif @@ -252,7 +252,7 @@ void SkARGB32_Blitter::blitRect(int x, int y, int width, int height) { } } -#if defined _WIN32 && _MSC_VER >= 1300 +#if defined _WIN32 #pragma warning ( pop ) #endif diff --git a/gfx/skia/skia/src/core/SkBlitter_PM4f.cpp b/gfx/skia/skia/src/core/SkBlitter_PM4f.cpp new file mode 100644 index 0000000000..820d72cc2e --- /dev/null +++ b/gfx/skia/skia/src/core/SkBlitter_PM4f.cpp @@ -0,0 +1,441 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkCoreBlitters.h" +#include "SkColorPriv.h" +#include "SkShader.h" +#include "SkUtils.h" +#include "SkXfermode.h" +#include "SkBlitMask.h" +#include "SkTemplates.h" +#include "SkPM4f.h" + +template class SkState_Blitter : public SkRasterBlitter { + typedef SkRasterBlitter INHERITED; + State fState; + +public: + SkState_Blitter(const SkPixmap& device, const SkPaint& paint) + : INHERITED(device) + , fState(device.info(), paint, nullptr) + {} + + void blitH(int x, int y, int width) override { + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); + + fState.fProc1(fState.fXfer, State::WritableAddr(fDevice, x, y), + &fState.fPM4f, width, nullptr); + } + + void blitV(int x, int y, int height, SkAlpha alpha) override { + SkASSERT(x >= 0 && y >= 0 && y + height <= fDevice.height()); + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + size_t deviceRB = fDevice.rowBytes(); + + for (int i = 0; i < height; ++i) { + fState.fProc1(fState.fXfer, device, &fState.fPM4f, 1, &alpha); + device = (typename State::DstType*)((char*)device + deviceRB); + } + } + + void blitRect(int x, int y, int width, int height) override { + SkASSERT(x >= 0 && y >= 0 && + x + width <= fDevice.width() && y + height <= fDevice.height()); + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + size_t deviceRB = fDevice.rowBytes(); + + do { + fState.fProc1(fState.fXfer, device, &fState.fPM4f, width, nullptr); + y += 1; + device = (typename State::DstType*)((char*)device + deviceRB); + } while (--height > 0); + } + + void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override { + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + int aa = *antialias; + if (aa) { + if (aa == 255) { + fState.fProc1(fState.fXfer, device, &fState.fPM4f, count, nullptr); + } else { + for (int i = 0; i < count; ++i) { + fState.fProc1(fState.fXfer, &device[i], &fState.fPM4f, 1, antialias); + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } + + void blitLCDMask(const SkMask& mask, const SkIRect& clip) { + auto proc = fState.getLCDProc(SkXfermode::kSrcIsSingle_LCDFlag); + + const int x = clip.fLeft; + const int width = clip.width(); + const int y = clip.fTop; + const int height = clip.height(); + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + const size_t dstRB = fDevice.rowBytes(); + const uint16_t* maskRow = (const uint16_t*)mask.getAddr(x, y); + const size_t maskRB = mask.fRowBytes; + + for (int i = 0; i < height; ++i) { + proc(device, &fState.fPM4f, width, maskRow); + device = (typename State::DstType*)((char*)device + dstRB); + maskRow = (const uint16_t*)((const char*)maskRow + maskRB); + } + } + + void blitMask(const SkMask& mask, const SkIRect& clip) override { + if (SkMask::kLCD16_Format == mask.fFormat) { + this->blitLCDMask(mask, clip); + return; + } + if (SkMask::kA8_Format != mask.fFormat) { + this->INHERITED::blitMask(mask, clip); + return; + } + + SkASSERT(mask.fBounds.contains(clip)); + + const int x = clip.fLeft; + const int width = clip.width(); + const int y = clip.fTop; + const int height = clip.height(); + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + const size_t dstRB = fDevice.rowBytes(); + const uint8_t* maskRow = (const uint8_t*)mask.getAddr(x, y); + const size_t maskRB = mask.fRowBytes; + + for (int i = 0; i < height; ++i) { + fState.fProc1(fState.fXfer, device, &fState.fPM4f, width, maskRow); + device = (typename State::DstType*)((char*)device + dstRB); + maskRow += maskRB; + } + } +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +template class SkState_Shader_Blitter : public SkShaderBlitter { +public: + SkState_Shader_Blitter(const SkPixmap& device, const SkPaint& paint, + const SkShader::Context::BlitState& bstate) + : INHERITED(device, paint, bstate.fCtx) + , fState(device.info(), paint, bstate.fCtx) + , fBState(bstate) + , fBlitBW(bstate.fBlitBW) + , fBlitAA(bstate.fBlitAA) + {} + + void blitH(int x, int y, int width) override { + SkASSERT(x >= 0 && y >= 0 && x + width <= fDevice.width()); + + if (fBlitBW) { + fBlitBW(&fBState, x, y, fDevice, width); + return; + } + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + fState.fProcN(fState.fXfer, device, fState.fBuffer, width, nullptr); + } + + void blitV(int x, int y, int height, SkAlpha alpha) override { + SkASSERT(x >= 0 && y >= 0 && y + height <= fDevice.height()); + + if (fBlitAA) { + for (const int bottom = y + height; y < bottom; ++y) { + fBlitAA(&fBState, x, y, fDevice, 1, &alpha); + } + return; + } + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + size_t deviceRB = fDevice.rowBytes(); + + if (fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, 1); + } + for (const int bottom = y + height; y < bottom; ++y) { + if (!fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, 1); + } + fState.fProcN(fState.fXfer, device, fState.fBuffer, 1, &alpha); + device = (typename State::DstType*)((char*)device + deviceRB); + } + } + + void blitRect(int x, int y, int width, int height) override { + SkASSERT(x >= 0 && y >= 0 && + x + width <= fDevice.width() && y + height <= fDevice.height()); + + if (fBlitBW) { + for (const int bottom = y + height; y < bottom; ++y) { + fBlitBW(&fBState, x, y, fDevice, width); + } + return; + } + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + size_t deviceRB = fDevice.rowBytes(); + + if (fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + } + for (const int bottom = y + height; y < bottom; ++y) { + if (!fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + } + fState.fProcN(fState.fXfer, device, fState.fBuffer, width, nullptr); + device = (typename State::DstType*)((char*)device + deviceRB); + } + } + + void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) override { + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + + for (;;) { + int count = *runs; + if (count <= 0) { + break; + } + int aa = *antialias; + if (aa) { + if (fBlitBW && (aa == 255)) { + fBlitBW(&fBState, x, y, fDevice, count); + } else { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, count); + if (aa == 255) { + fState.fProcN(fState.fXfer, device, fState.fBuffer, count, nullptr); + } else { + for (int i = 0; i < count; ++i) { + fState.fProcN(fState.fXfer, &device[i], &fState.fBuffer[i], 1, antialias); + } + } + } + } + device += count; + runs += count; + antialias += count; + x += count; + } + } + + void blitLCDMask(const SkMask& mask, const SkIRect& clip) { + auto proc = fState.getLCDProc(0); + + const int x = clip.fLeft; + const int width = clip.width(); + int y = clip.fTop; + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + const size_t deviceRB = fDevice.rowBytes(); + const uint16_t* maskRow = (const uint16_t*)mask.getAddr(x, y); + const size_t maskRB = mask.fRowBytes; + + if (fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + } + for (; y < clip.fBottom; ++y) { + if (!fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + } + proc(device, fState.fBuffer, width, maskRow); + device = (typename State::DstType*)((char*)device + deviceRB); + maskRow = (const uint16_t*)((const char*)maskRow + maskRB); + } + } + + void blitMask(const SkMask& mask, const SkIRect& clip) override { + if (SkMask::kLCD16_Format == mask.fFormat) { + this->blitLCDMask(mask, clip); + return; + } + if (SkMask::kA8_Format != mask.fFormat) { + this->INHERITED::blitMask(mask, clip); + return; + } + + SkASSERT(mask.fBounds.contains(clip)); + + const int x = clip.fLeft; + const int width = clip.width(); + int y = clip.fTop; + const uint8_t* maskRow = (const uint8_t*)mask.getAddr(x, y); + const size_t maskRB = mask.fRowBytes; + + if (fBlitAA) { + for (; y < clip.fBottom; ++y) { + fBlitAA(&fBState, x, y, fDevice, width, maskRow); + maskRow += maskRB; + } + return; + } + + typename State::DstType* device = State::WritableAddr(fDevice, x, y); + const size_t deviceRB = fDevice.rowBytes(); + + if (fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + } + for (; y < clip.fBottom; ++y) { + if (!fConstInY) { + fShaderContext->shadeSpan4f(x, y, fState.fBuffer, width); + } + fState.fProcN(fState.fXfer, device, fState.fBuffer, width, maskRow); + device = (typename State::DstType*)((char*)device + deviceRB); + maskRow += maskRB; + } + } + +protected: + State fState; + SkShader::Context::BlitState fBState; + SkShader::Context::BlitBW fBlitBW; + SkShader::Context::BlitAA fBlitAA; + + typedef SkShaderBlitter INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +static bool is_opaque(const SkPaint& paint, const SkShader::Context* shaderContext) { + return shaderContext ? SkToBool(shaderContext->getFlags() & SkShader::kOpaqueAlpha_Flag) + : 0xFF == paint.getAlpha(); +} + +struct State4f { + State4f(const SkImageInfo& info, const SkPaint& paint, const SkShader::Context* shaderContext) { + fXfer = paint.getXfermode(); + if (shaderContext) { + fBuffer.reset(info.width()); + } else { + fPM4f = SkColor4f::FromColor(paint.getColor()).premul(); + } + fFlags = 0; + } + + SkXfermode* fXfer; + SkPM4f fPM4f; + SkAutoTMalloc fBuffer; + uint32_t fFlags; + + SkShader::Context::BlitState fBState; +}; + +struct State32 : State4f { + typedef uint32_t DstType; + + SkXfermode::D32Proc fProc1; + SkXfermode::D32Proc fProcN; + + State32(const SkImageInfo& info, const SkPaint& paint, const SkShader::Context* shaderContext) + : State4f(info, paint, shaderContext) + { + if (is_opaque(paint, shaderContext)) { + fFlags |= SkXfermode::kSrcIsOpaque_D32Flag; + } + if (info.isSRGB()) { + fFlags |= SkXfermode::kDstIsSRGB_D32Flag; + } + fProc1 = SkXfermode::GetD32Proc(fXfer, fFlags | SkXfermode::kSrcIsSingle_D32Flag); + fProcN = SkXfermode::GetD32Proc(fXfer, fFlags); + } + + SkXfermode::LCD32Proc getLCDProc(uint32_t oneOrManyFlag) const { + uint32_t flags = fFlags & 1; + if (!(fFlags & SkXfermode::kDstIsSRGB_D32Flag)) { + flags |= SkXfermode::kDstIsLinearInt_LCDFlag; + } + return SkXfermode::GetLCD32Proc(flags | oneOrManyFlag); + } + + static DstType* WritableAddr(const SkPixmap& device, int x, int y) { + return device.writable_addr32(x, y); + } +}; + +struct State64 : State4f { + typedef uint64_t DstType; + + SkXfermode::D64Proc fProc1; + SkXfermode::D64Proc fProcN; + + State64(const SkImageInfo& info, const SkPaint& paint, const SkShader::Context* shaderContext) + : State4f(info, paint, shaderContext) + { + if (is_opaque(paint, shaderContext)) { + fFlags |= SkXfermode::kSrcIsOpaque_D64Flag; + } + if (kRGBA_F16_SkColorType == info.colorType()) { + fFlags |= SkXfermode::kDstIsFloat16_D64Flag; + } + fProc1 = SkXfermode::GetD64Proc(fXfer, fFlags | SkXfermode::kSrcIsSingle_D64Flag); + fProcN = SkXfermode::GetD64Proc(fXfer, fFlags); + } + + SkXfermode::LCD64Proc getLCDProc(uint32_t oneOrManyFlag) const { + uint32_t flags = fFlags & 1; + if (!(fFlags & SkXfermode::kDstIsFloat16_D64Flag)) { + flags |= SkXfermode::kDstIsLinearInt_LCDFlag; + } + return SkXfermode::GetLCD64Proc(flags | oneOrManyFlag); + } + + static DstType* WritableAddr(const SkPixmap& device, int x, int y) { + return device.writable_addr64(x, y); + } +}; + +template SkBlitter* create(const SkPixmap& device, const SkPaint& paint, + SkShader::Context* shaderContext, + SkTBlitterAllocator* allocator) { + SkASSERT(allocator != nullptr); + + if (shaderContext) { + SkShader::Context::BlitState bstate; + sk_bzero(&bstate, sizeof(bstate)); + bstate.fCtx = shaderContext; + bstate.fXfer = paint.getXfermode(); + + (void)shaderContext->chooseBlitProcs(device.info(), &bstate); + return allocator->createT>(device, paint, bstate); + } else { + SkColor color = paint.getColor(); + if (0 == SkColorGetA(color)) { + return nullptr; + } + return allocator->createT>(device, paint); + } +} + +SkBlitter* SkBlitter_ARGB32_Create(const SkPixmap& device, const SkPaint& paint, + SkShader::Context* shaderContext, + SkTBlitterAllocator* allocator) { + return create(device, paint, shaderContext, allocator); +} + +SkBlitter* SkBlitter_ARGB64_Create(const SkPixmap& device, const SkPaint& paint, + SkShader::Context* shaderContext, + SkTBlitterAllocator* allocator) { + return create(device, paint, shaderContext, allocator); +} diff --git a/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp b/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp index 605fa43bf6..27cbd61768 100644 --- a/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp +++ b/gfx/skia/skia/src/core/SkBlitter_Sprite.cpp @@ -60,7 +60,14 @@ SkBlitter* SkBlitter::ChooseSprite(const SkPixmap& dst, const SkPaint& paint, blitter = SkSpriteBlitter::ChooseD16(source, paint, allocator); break; case kN32_SkColorType: - blitter = SkSpriteBlitter::ChooseD32(source, paint, allocator); + if (dst.info().isSRGB()) { + blitter = SkSpriteBlitter::ChooseS32(source, paint, allocator); + } else { + blitter = SkSpriteBlitter::ChooseL32(source, paint, allocator); + } + break; + case kRGBA_F16_SkColorType: + blitter = SkSpriteBlitter::ChooseF16(source, paint, allocator); break; default: blitter = nullptr; diff --git a/gfx/skia/skia/src/core/SkBuffer.h b/gfx/skia/skia/src/core/SkBuffer.h index 92ed7059db..c466fb65ea 100644 --- a/gfx/skia/skia/src/core/SkBuffer.h +++ b/gfx/skia/skia/src/core/SkBuffer.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkCachedData.cpp b/gfx/skia/skia/src/core/SkCachedData.cpp index cfa4c6165f..1ea232b2c5 100644 --- a/gfx/skia/skia/src/core/SkCachedData.cpp +++ b/gfx/skia/skia/src/core/SkCachedData.cpp @@ -98,7 +98,7 @@ void SkCachedData::inMutexRef(bool fromCache) { if ((1 == fRefCnt) && fInCache) { this->inMutexLock(); } - + fRefCnt += 1; if (fromCache) { SkASSERT(!fInCache); @@ -125,22 +125,22 @@ bool SkCachedData::inMutexUnref(bool fromCache) { default: break; } - + if (fromCache) { SkASSERT(fInCache); fInCache = false; } - + // return true when we need to be deleted return 0 == fRefCnt; } void SkCachedData::inMutexLock() { fMutex.assertHeld(); - + SkASSERT(!fIsLocked); fIsLocked = true; - + switch (fStorageType) { case kMalloc_StorageType: this->setData(fStorage.fMalloc); @@ -159,10 +159,10 @@ void SkCachedData::inMutexLock() { void SkCachedData::inMutexUnlock() { fMutex.assertHeld(); - + SkASSERT(fIsLocked); fIsLocked = false; - + switch (fStorageType) { case kMalloc_StorageType: // nothing to do/check diff --git a/gfx/skia/skia/src/core/SkCanvas.cpp b/gfx/skia/skia/src/core/SkCanvas.cpp index e577b21932..de69c368cd 100644 --- a/gfx/skia/skia/src/core/SkCanvas.cpp +++ b/gfx/skia/skia/src/core/SkCanvas.cpp @@ -28,6 +28,7 @@ #include "SkReadPixelsRec.h" #include "SkRRect.h" #include "SkSmallAllocator.h" +#include "SkSpecialImage.h" #include "SkSurface_Base.h" #include "SkTextBlob.h" #include "SkTextFormatParams.h" @@ -390,16 +391,17 @@ static SkPaint* set_if_needed(SkLazyPaint* lazy, const SkPaint& orig) { * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that * colorfilter, else return nullptr. */ -static SkColorFilter* image_to_color_filter(const SkPaint& paint) { +static sk_sp image_to_color_filter(const SkPaint& paint) { SkImageFilter* imgf = paint.getImageFilter(); if (!imgf) { return nullptr; } - SkColorFilter* imgCF; - if (!imgf->asAColorFilter(&imgCF)) { + SkColorFilter* imgCFPtr; + if (!imgf->asAColorFilter(&imgCFPtr)) { return nullptr; } + sk_sp imgCF(imgCFPtr); SkColorFilter* paintCF = paint.getColorFilter(); if (nullptr == paintCF) { @@ -409,8 +411,7 @@ static SkColorFilter* image_to_color_filter(const SkPaint& paint) { // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF) // and we need to combine them into a single colorfilter. - SkAutoTUnref autoImgCF(imgCF); - return SkColorFilter::CreateComposeFilter(imgCF, paintCF); + return SkColorFilter::MakeComposeFilter(std::move(imgCF), sk_ref_sp(paintCF)); } /** @@ -444,16 +445,20 @@ public: bool skipLayerForImageFilter = false, const SkRect* rawBounds = nullptr) : fOrigPaint(paint) { fCanvas = canvas; +#ifdef SK_SUPPORT_LEGACY_DRAWFILTER fFilter = canvas->getDrawFilter(); +#else + fFilter = nullptr; +#endif fPaint = &fOrigPaint; fSaveCount = canvas->getSaveCount(); fTempLayerForImageFilter = false; fDone = false; - SkColorFilter* simplifiedCF = image_to_color_filter(fOrigPaint); + auto simplifiedCF = image_to_color_filter(fOrigPaint); if (simplifiedCF) { SkPaint* paint = set_if_needed(&fLazyPaintInit, fOrigPaint); - paint->setColorFilter(simplifiedCF)->unref(); + paint->setColorFilter(std::move(simplifiedCF)); paint->setImageFilter(nullptr); fPaint = paint; } @@ -476,7 +481,7 @@ public: */ SkPaint tmp; tmp.setImageFilter(fPaint->getImageFilter()); - tmp.setXfermode(fPaint->getXfermode()); + tmp.setXfermode(sk_ref_sp(fPaint->getXfermode())); SkRect storage; if (rawBounds) { // Make rawBounds include all paint outsets except for those due to image filters. @@ -778,6 +783,7 @@ SkCanvas::~SkCanvas() { dec_canvas(); } +#ifdef SK_SUPPORT_LEGACY_DRAWFILTER SkDrawFilter* SkCanvas::getDrawFilter() const { return fMCRec->fFilter; } @@ -787,6 +793,7 @@ SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) { SkRefCnt_SafeAssign(fMCRec->fFilter, filter); return filter; } +#endif SkMetaData& SkCanvas::getMetaData() { // metadata users are rare, so we lazily allocate it. If that changes we @@ -806,21 +813,19 @@ void SkCanvas::flush() { } } -SkISize SkCanvas::getTopLayerSize() const { - SkBaseDevice* d = this->getTopDevice(); - return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0); -} - -SkIPoint SkCanvas::getTopLayerOrigin() const { - SkBaseDevice* d = this->getTopDevice(); - return d ? d->getOrigin() : SkIPoint::Make(0, 0); -} - SkISize SkCanvas::getBaseLayerSize() const { SkBaseDevice* d = this->getDevice(); return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0); } +SkIRect SkCanvas::getTopLayerBounds() const { + SkBaseDevice* d = this->getTopDevice(); + if (!d) { + return SkIRect::MakeEmpty(); + } + return SkIRect::MakeXYWH(d->getOrigin().x(), d->getOrigin().y(), d->width(), d->height()); +} + SkBaseDevice* SkCanvas::getDevice() const { // return root device MCRec* rec = (MCRec*) fMCStack.front(); @@ -1071,25 +1076,11 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlag const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix() -// This is a temporary hack, until individual filters can do their own -// bloating, when this will be removed. -#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS - SkRect storage; -#endif if (imageFilter) { - imageFilter->filterBounds(clipBounds, ctm, &clipBounds); -#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS - if (bounds && imageFilter->canComputeFastBounds()) { - imageFilter->computeFastBounds(*bounds, &storage); - bounds = &storage; - } else { - bounds = nullptr; - } -#else + clipBounds = imageFilter->filterBounds(clipBounds, ctm); if (bounds && !imageFilter->canComputeFastBounds()) { bounds = nullptr; } -#endif } SkIRect ir; if (bounds) { @@ -1123,42 +1114,11 @@ bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlag return true; } -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS -uint32_t SkCanvas::SaveFlagsToSaveLayerFlags(SaveFlags flags) { - uint32_t layerFlags = 0; - - if (0 == (flags & kClipToLayer_SaveFlag)) { - layerFlags |= kDontClipToLayer_PrivateSaveLayerFlag; - } - if (0 == (flags & kHasAlphaLayer_SaveFlag)) { - layerFlags |= kIsOpaque_SaveLayerFlag; - } - return layerFlags; -} - -uint32_t SkCanvas::SaveLayerFlagsToSaveFlags(SaveLayerFlags layerFlags) { - uint32_t saveFlags = 0; - - if (0 == (layerFlags & kDontClipToLayer_PrivateSaveLayerFlag)) { - saveFlags |= kClipToLayer_SaveFlag; - } - if (0 == (layerFlags & kIsOpaque_SaveLayerFlag)) { - saveFlags |= kHasAlphaLayer_SaveFlag; - } - return saveFlags; -} -#endif int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) { return this->saveLayer(SaveLayerRec(bounds, paint, 0)); } -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS -int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint, SaveFlags flags) { - return this->saveLayer(SaveLayerRec(bounds, paint, SaveFlagsToSaveLayerFlags(flags))); -} -#endif - int SkCanvas::saveLayerPreserveLCDTextRequests(const SkRect* bounds, const SkPaint* paint) { return this->saveLayer(SaveLayerRec(bounds, paint, kPreserveLCDText_SaveLayerFlag)); } @@ -1187,7 +1147,8 @@ static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filt // TODO: we should actually only copy the portion of the source needed to apply the image // filter GrContext* context = srcRT->getContext(); - SkAutoTUnref tex(context->textureProvider()->createTexture(srcRT->desc(), true)); + SkAutoTUnref tex(context->textureProvider()->createTexture(srcRT->desc(), + SkBudgeted::kYes)); context->copySurface(tex, srcRT); @@ -1200,14 +1161,29 @@ static void draw_filter_into_device(SkBaseDevice* src, const SkImageFilter* filt SkCanvas c(dst); - SkAutoTUnref localF(filter->newWithLocalMatrix(ctm)); SkPaint p; - p.setImageFilter(localF); + p.setImageFilter(filter->makeWithLocalMatrix(ctm)); const SkScalar x = SkIntToScalar(src->getOrigin().x()); const SkScalar y = SkIntToScalar(src->getOrigin().y()); c.drawBitmap(srcBM, x, y, &p); } +static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, bool isOpaque, + const SkPaint* paint) { + // need to force L32 for now if we have an image filter. Once filters support other colortypes + // e.g. sRGB or F16, we can remove this check + const bool hasImageFilter = paint && paint->getImageFilter(); + + SkAlphaType alphaType = isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType; + if ((prev.bytesPerPixel() < 4) || hasImageFilter) { + // force to L32 + return SkImageInfo::MakeN32(w, h, alphaType); + } else { + // keep the same characteristics as the prev + return SkImageInfo::Make(w, h, prev.colorType(), alphaType, prev.profileType()); + } +} + void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) { const SkRect* bounds = rec.fBounds; const SkPaint* paint = rec.fPaint; @@ -1243,8 +1219,6 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra geo = kUnknown_SkPixelGeometry; } } - SkImageInfo info = SkImageInfo::MakeN32(ir.width(), ir.height(), - isOpaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); SkBaseDevice* device = this->getTopDevice(); if (nullptr == device) { @@ -1252,6 +1226,9 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra return; } + SkImageInfo info = make_layer_info(device->imageInfo(), ir.width(), ir.height(), isOpaque, + paint); + bool forceSpriteOnRestore = false; { const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() || @@ -1298,19 +1275,6 @@ int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) { } } -#ifdef SK_SUPPORT_LEGACY_SAVEFLAGS -int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha, - SaveFlags flags) { - if (0xFF == alpha) { - return this->saveLayer(bounds, nullptr, flags); - } else { - SkPaint tmpPaint; - tmpPaint.setAlpha(alpha); - return this->saveLayer(bounds, &tmpPaint, flags); - } -} -#endif - void SkCanvas::internalRestore() { SkASSERT(fMCStack.count() != 0); @@ -1349,16 +1313,16 @@ void SkCanvas::internalRestore() { } } -SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) { +sk_sp SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) { if (nullptr == props) { props = &fProps; } return this->onNewSurface(info, *props); } -SkSurface* SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) { +sk_sp SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) { SkBaseDevice* dev = this->getDevice(); - return dev ? dev->newSurface(info, props) : nullptr; + return dev ? dev->makeSurface(info, props) : nullptr; } SkImageInfo SkCanvas::imageInfo() const { @@ -1370,18 +1334,36 @@ SkImageInfo SkCanvas::imageInfo() const { } } +bool SkCanvas::getProps(SkSurfaceProps* props) const { + SkBaseDevice* dev = this->getDevice(); + if (dev) { + if (props) { + *props = fProps; + } + return true; + } else { + return false; + } +} + +#ifdef SK_SUPPORT_LEGACY_PEEKPIXELS_PARMS const void* SkCanvas::peekPixels(SkImageInfo* info, size_t* rowBytes) { SkPixmap pmap; - if (!this->onPeekPixels(&pmap)) { - return nullptr; + if (this->peekPixels(&pmap)) { + if (info) { + *info = pmap.info(); + } + if (rowBytes) { + *rowBytes = pmap.rowBytes(); + } + return pmap.addr(); } - if (info) { - *info = pmap.info(); - } - if (rowBytes) { - *rowBytes = pmap.rowBytes(); - } - return pmap.addr(); + return nullptr; +} +#endif + +bool SkCanvas::peekPixels(SkPixmap* pmap) { + return this->onPeekPixels(pmap); } bool SkCanvas::onPeekPixels(SkPixmap* pmap) { @@ -1428,23 +1410,30 @@ void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, int x, int y, SkIPoint pos = { x - iter.getX(), y - iter.getY() }; if (filter && !dstDev->canHandleImageFilter(filter)) { SkImageFilter::DeviceProxy proxy(dstDev); - SkBitmap dst; SkIPoint offset = SkIPoint::Make(0, 0); - const SkBitmap& src = srcDev->accessBitmap(false); + const SkBitmap& srcBM = srcDev->accessBitmap(false); SkMatrix matrix = *iter.fMatrix; matrix.postTranslate(SkIntToScalar(-pos.x()), SkIntToScalar(-pos.y())); -#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS - SkIRect clipBounds = SkIRect::MakeWH(srcDev->width(), srcDev->height()); -#else - SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y()); -#endif + const SkIRect clipBounds = iter.fClip->getBounds().makeOffset(-pos.x(), -pos.y()); SkAutoTUnref cache(dstDev->getImageFilterCache()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get()); - if (filter->filterImage(&proxy, src, ctx, &dst, &offset)) { + + sk_sp srcImg(SkSpecialImage::internal_fromBM(&proxy, srcBM, + &dstDev->surfaceProps())); + if (!srcImg) { + continue; // something disastrous happened + } + + sk_sp resultImg(filter->filterImage(srcImg.get(), ctx, &offset)); + if (resultImg) { SkPaint tmpUnfiltered(*paint); tmpUnfiltered.setImageFilter(nullptr); - dstDev->drawSprite(iter, dst, pos.x() + offset.x(), pos.y() + offset.y(), - tmpUnfiltered); + SkBitmap resultBM; + if (resultImg->internal_getBM(&resultBM)) { + // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073) + dstDev->drawSprite(iter, resultBM, pos.x() + offset.x(), pos.y() + offset.y(), + tmpUnfiltered); + } } } else if (deviceIsBitmapDevice) { const SkBitmap& src = static_cast(srcDev)->fBitmap; @@ -1572,7 +1561,7 @@ void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edg if (rectStaysRect) { const bool isAA = kSoft_ClipEdgeStyle == edgeStyle; fClipStack->clipDevRect(devR, op, isAA); - fMCRec->fRasterClip.op(devR, this->getBaseLayerSize(), op, isAA); + fMCRec->fRasterClip.op(devR, this->getTopLayerBounds(), op, isAA); } else { // since we're rotated or some such thing, we convert the rect to a path // and clip against that, since it can handle any matrix. However, to @@ -1585,11 +1574,6 @@ void SkCanvas::onClipRect(const SkRect& rect, SkRegion::Op op, ClipEdgeStyle edg } } -static void rasterclip_path(SkRasterClip* rc, const SkCanvas* canvas, const SkPath& devPath, - SkRegion::Op op, bool doAA) { - rc->op(devPath, canvas->getBaseLayerSize(), op, doAA); -} - void SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) { this->checkForDeferredSave(); ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; @@ -1613,7 +1597,7 @@ void SkCanvas::onClipRRect(const SkRRect& rrect, SkRegion::Op op, ClipEdgeStyle fClipStack->clipDevRRect(transformedRRect, op, kSoft_ClipEdgeStyle == edgeStyle); - fMCRec->fRasterClip.op(transformedRRect, this->getBaseLayerSize(), op, + fMCRec->fRasterClip.op(transformedRRect, this->getTopLayerBounds(), op, kSoft_ClipEdgeStyle == edgeStyle); return; } @@ -1699,7 +1683,7 @@ void SkCanvas::onClipPath(const SkPath& path, SkRegion::Op op, ClipEdgeStyle edg op = SkRegion::kReplace_Op; } - rasterclip_path(&fMCRec->fRasterClip, this, devPath, op, edgeStyle); + fMCRec->fRasterClip.op(devPath, this->getTopLayerBounds(), op, edgeStyle); } void SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) { @@ -1747,7 +1731,7 @@ void SkCanvas::validateClip() const { default: { SkPath path; element->asPath(&path); - rasterclip_path(&tmpClip, this, path, element->getOp(), element->isAA()); + tmpClip.op(path, this->getTopLayerBounds(), element->getOp(), element->isAA()); break; } } @@ -2018,6 +2002,12 @@ void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const Sk this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint); } +void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) { + if (key) { + this->onDrawAnnotation(rect, key, value); + } +} + void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { if (src) { @@ -2257,14 +2247,26 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S return; } } - + SkLazyPaint lazy; if (nullptr == paint) { paint = lazy.init(); } - const bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), - *paint); + bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), + *paint); + if (drawAsSprite && paint->getImageFilter()) { + SkBitmap bitmap; + if (!as_IB(image)->asBitmapForImageFilters(&bitmap)) { + drawAsSprite = false; + } else{ + // Until imagefilters are updated, they cannot handle any src type but N32... + if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) { + drawAsSprite = false; + } + } + } + LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds) while (iter.next()) { @@ -2274,15 +2276,15 @@ void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const S if (as_IB(image)->asBitmapForImageFilters(&bitmap)) { SkPoint pt; iter.fMatrix->mapXY(x, y, &pt); - iter.fDevice->drawBitmapAsSprite(iter, bitmap, - SkScalarRoundToInt(pt.fX), - SkScalarRoundToInt(pt.fY), pnt); + iter.fDevice->drawSpriteWithFilter(iter, bitmap, + SkScalarRoundToInt(pt.fX), + SkScalarRoundToInt(pt.fY), pnt); } } else { iter.fDevice->drawImage(iter, image, x, y, pnt); } } - + LOOPER_END } @@ -2302,14 +2304,14 @@ void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const Sk if (nullptr == paint) { paint = lazy.init(); } - + LOOPER_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, SkDrawFilter::kBitmap_Type, &dst, image->isOpaque()) - + while (iter.next()) { iter.fDevice->drawImageRect(iter, image, src, dst, looper.paint(), constraint); } - + LOOPER_END } @@ -2340,8 +2342,15 @@ void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, cons bounds = &storage; } - const bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), - bitmap.height(), *paint); + bool drawAsSprite = bounds && this->canDrawBitmapAsSprite(x, y, bitmap.width(), bitmap.height(), + *paint); + if (drawAsSprite && paint->getImageFilter()) { + // Until imagefilters are updated, they cannot handle any src type but N32... + if (bitmap.info().colorType() != kN32_SkColorType || bitmap.info().isSRGB()) { + drawAsSprite = false; + } + } + LOOPER_BEGIN_DRAWBITMAP(*paint, drawAsSprite, bounds) while (iter.next()) { @@ -2349,9 +2358,9 @@ void SkCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, cons if (drawAsSprite && pnt.getImageFilter()) { SkPoint pt; iter.fMatrix->mapXY(x, y, &pt); - iter.fDevice->drawBitmapAsSprite(iter, bitmap, - SkScalarRoundToInt(pt.fX), - SkScalarRoundToInt(pt.fY), pnt); + iter.fDevice->drawSpriteWithFilter(iter, bitmap, + SkScalarRoundToInt(pt.fX), + SkScalarRoundToInt(pt.fY), pnt); } else { iter.fDevice->drawBitmap(iter, bitmap, matrix, looper.paint()); } @@ -2400,25 +2409,25 @@ void SkCanvas::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, const SkPaint* paint) { TRACE_EVENT0("disabled-by-default-skia", "SkCanvas::drawImageNine()"); - + if (nullptr == paint || paint->canComputeFastBounds()) { SkRect storage; if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) { return; } } - + SkLazyPaint lazy; if (nullptr == paint) { paint = lazy.init(); } - + LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst) - + while (iter.next()) { iter.fDevice->drawImageNine(iter, image, center, dst, looper.paint()); } - + LOOPER_END } @@ -2433,18 +2442,18 @@ void SkCanvas::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, c return; } } - + SkLazyPaint lazy; if (nullptr == paint) { paint = lazy.init(); } - + LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type, &dst) - + while (iter.next()) { iter.fDevice->drawBitmapNine(iter, bitmap, center, dst, looper.paint()); } - + LOOPER_END } @@ -2743,7 +2752,7 @@ void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const if (paint) { pnt = *paint; } - + LOOPER_BEGIN(pnt, SkDrawFilter::kPath_Type, nullptr) while (iter.next()) { iter.fDevice->drawAtlas(iter, atlas, xform, tex, colors, count, mode, pnt); @@ -2751,6 +2760,17 @@ void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const LOOPER_END } +void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { + SkASSERT(key); + + SkPaint paint; + LOOPER_BEGIN(paint, SkDrawFilter::kRect_Type, nullptr) + while (iter.next()) { + iter.fDevice->drawAnnotation(iter, rect, key, value); + } + LOOPER_END +} + ////////////////////////////////////////////////////////////////////////////// // These methods are NOT virtual, and therefore must call back into virtual // methods, rather than actually drawing themselves. @@ -3034,3 +3054,9 @@ SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatri SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() { fCanvas->restoreToCount(fSaveCount); } + +#ifdef SK_SUPPORT_LEGACY_NEW_SURFACE_API +SkSurface* SkCanvas::newSurface(const SkImageInfo& info, const SkSurfaceProps* props) { + return this->makeSurface(info, props).release(); +} +#endif diff --git a/gfx/skia/skia/src/core/SkChunkAlloc.cpp b/gfx/skia/skia/src/core/SkChunkAlloc.cpp index 3f30276f1b..8c5b8fb6a4 100644 --- a/gfx/skia/skia/src/core/SkChunkAlloc.cpp +++ b/gfx/skia/skia/src/core/SkChunkAlloc.cpp @@ -23,7 +23,7 @@ struct SkChunkAlloc::Block { char* fFreePtr; // data[] follows - size_t blockSize() { + size_t blockSize() { char* start = this->startOfData(); size_t bytes = fFreePtr - start; return fFreeSize + bytes; @@ -232,4 +232,3 @@ void SkChunkAlloc::validate() { SkASSERT(totCapacity == totUsed + totLost + totAvailable); } #endif - diff --git a/gfx/skia/skia/src/core/SkClipStack.cpp b/gfx/skia/skia/src/core/SkClipStack.cpp index 4227eeac68..4e53d8b850 100644 --- a/gfx/skia/skia/src/core/SkClipStack.cpp +++ b/gfx/skia/skia/src/core/SkClipStack.cpp @@ -21,6 +21,7 @@ int32_t SkClipStack::gGenID = kFirstUnreservedGenID; SkClipStack::Element::Element(const Element& that) { switch (that.getType()) { case kEmpty_Type: + fRRect.setEmpty(); fPath.reset(); break; case kRect_Type: // Rect uses rrect @@ -171,6 +172,7 @@ void SkClipStack::Element::checkEmpty() const { SkASSERT(kNormal_BoundsType == fFiniteBoundType); SkASSERT(!fIsIntersectionOfRects); SkASSERT(kEmptyGenID == fGenID); + SkASSERT(fRRect.isEmpty()); SkASSERT(!fPath.isValid()); } diff --git a/gfx/skia/skia/src/core/SkColor.cpp b/gfx/skia/skia/src/core/SkColor.cpp index a21f019239..ab63300119 100644 --- a/gfx/skia/skia/src/core/SkColor.cpp +++ b/gfx/skia/skia/src/core/SkColor.cpp @@ -100,3 +100,78 @@ SkColor SkHSVToColor(U8CPU a, const SkScalar hsv[3]) { } return SkColorSetARGB(a, r, g, b); } + +/////////////////////////////////////////////////////////////////////////////////////////////////// +#include "SkPM4fPriv.h" +#include "SkHalf.h" + +SkPM4f SkPM4f::FromPMColor(SkPMColor c) { + Sk4f value = to_4f_rgba(c); + SkPM4f c4; + (value * Sk4f(1.0f / 255)).store(&c4); + return c4; +} + +SkColor4f SkPM4f::unpremul() const { + float alpha = fVec[A]; + if (0 == alpha) { + return { 0, 0, 0, 0 }; + } else { + float invAlpha = 1 / alpha; + return { alpha, fVec[R] * invAlpha, fVec[G] * invAlpha, fVec[B] * invAlpha }; + } +} + +void SkPM4f::toF16(uint16_t half[4]) const { + for (int i = 0; i < 4; ++i) { + half[i] = SkFloatToHalf(fVec[i]); + } +} + +uint64_t SkPM4f::toF16() const { + uint64_t value; + this->toF16(reinterpret_cast(&value)); + return value; +} + +SkPM4f SkPM4f::FromF16(const uint16_t half[4]) { + return {{ + SkHalfToFloat(half[0]), + SkHalfToFloat(half[1]), + SkHalfToFloat(half[2]), + SkHalfToFloat(half[3]) + }}; +} + +#ifdef SK_DEBUG +void SkPM4f::assertIsUnit() const { + auto c4 = Sk4f::Load(fVec); + SkASSERT((c4 >= Sk4f(0)).allTrue() && (c4 <= Sk4f(1)).allTrue()); +} +#endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkColor4f SkColor4f::FromColor(SkColor c) { + Sk4f value = SkNx_shuffle<3,2,1,0>(SkNx_cast(Sk4b::Load(&c))); + SkColor4f c4; + (value * Sk4f(1.0f / 255)).store(&c4); + return c4; +} + +SkColor4f SkColor4f::Pin(float a, float r, float g, float b) { + SkColor4f c4; + Sk4f::Min(Sk4f::Max(Sk4f(a, r, g, b), Sk4f(0)), Sk4f(1)).store(c4.vec()); + return c4; +} + +SkPM4f SkColor4f::premul() const { + auto src = Sk4f::Load(this->pin().vec()); + float srcAlpha = src[0]; // need the pinned version of our alpha + src = src * Sk4f(1, srcAlpha, srcAlpha, srcAlpha); + + // ARGB -> RGBA + Sk4f dst = SkNx_shuffle<1,2,3,0>(src); + + return SkPM4f::From4f(dst); +} diff --git a/gfx/skia/skia/src/core/SkColorFilter.cpp b/gfx/skia/skia/src/core/SkColorFilter.cpp index 747e5ee107..e3d8957947 100644 --- a/gfx/skia/skia/src/core/SkColorFilter.cpp +++ b/gfx/skia/skia/src/core/SkColorFilter.cpp @@ -12,6 +12,8 @@ #include "SkTDArray.h" #include "SkUnPreMultiply.h" #include "SkWriteBuffer.h" +#include "SkPM4f.h" +#include "SkNx.h" #if SK_SUPPORT_GPU #include "GrFragmentProcessor.h" @@ -29,12 +31,35 @@ bool SkColorFilter::asComponentTable(SkBitmap*) const { return false; } +void SkColorFilter::filterSpan4f(const SkPM4f[], int count, SkPM4f span[]) const { + const int N = 128; + SkPMColor tmp[N]; + while (count > 0) { + int n = SkTMin(count, N); + for (int i = 0; i < n; ++i) { + SkNx_cast(Sk4f::Load(span[i].fVec) * Sk4f(255) + Sk4f(0.5f)).store(&tmp[i]); + } + this->filterSpan(tmp, n, tmp); + for (int i = 0; i < n; ++i) { + span[i] = SkPM4f::FromPMColor(tmp[i]); + } + span += n; + count -= n; + } +} + SkColor SkColorFilter::filterColor(SkColor c) const { SkPMColor dst, src = SkPreMultiplyColor(c); this->filterSpan(&src, 1, &dst); return SkUnPreMultiply::PMColorToColor(dst); } +SkColor4f SkColorFilter::filterColor4f(const SkColor4f& c) const { + SkPM4f dst, src = c.premul(); + this->filterSpan4f(&src, 1, &dst); + return dst.unpremul(); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// /* @@ -50,15 +75,20 @@ SkColor SkColorFilter::filterColor(SkColor c) const { class SkComposeColorFilter : public SkColorFilter { public: uint32_t getFlags() const override { - // Can only claim alphaunchanged and 16bit support if both our proxys do. + // Can only claim alphaunchanged and SkPM4f support if both our proxys do. return fOuter->getFlags() & fInner->getFlags(); } - + void filterSpan(const SkPMColor shader[], int count, SkPMColor result[]) const override { fInner->filterSpan(shader, count, result); fOuter->filterSpan(result, count, result); } - + + void filterSpan4f(const SkPM4f shader[], int count, SkPM4f result[]) const override { + fInner->filterSpan4f(shader, count, result); + fOuter->filterSpan4f(result, count, result); + } + #ifndef SK_IGNORE_TO_STRING void toString(SkString* str) const override { SkString outerS, innerS; @@ -81,17 +111,18 @@ public: #endif SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeColorFilter) - + protected: void flatten(SkWriteBuffer& buffer) const override { - buffer.writeFlattenable(fOuter); - buffer.writeFlattenable(fInner); + buffer.writeFlattenable(fOuter.get()); + buffer.writeFlattenable(fInner.get()); } - + private: - SkComposeColorFilter(SkColorFilter* outer, SkColorFilter* inner, int composedFilterCount) - : fOuter(SkRef(outer)) - , fInner(SkRef(inner)) + SkComposeColorFilter(sk_sp outer, sk_sp inner, + int composedFilterCount) + : fOuter(std::move(outer)) + , fInner(std::move(inner)) , fComposedFilterCount(composedFilterCount) { SkASSERT(composedFilterCount >= 2); @@ -102,33 +133,34 @@ private: return fComposedFilterCount; } - SkAutoTUnref fOuter; - SkAutoTUnref fInner; - const int fComposedFilterCount; + sk_sp fOuter; + sk_sp fInner; + const int fComposedFilterCount; friend class SkColorFilter; typedef SkColorFilter INHERITED; }; -SkFlattenable* SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) { - SkAutoTUnref outer(buffer.readColorFilter()); - SkAutoTUnref inner(buffer.readColorFilter()); - return CreateComposeFilter(outer, inner); +sk_sp SkComposeColorFilter::CreateProc(SkReadBuffer& buffer) { + sk_sp outer(buffer.readColorFilter()); + sk_sp inner(buffer.readColorFilter()); + return MakeComposeFilter(std::move(outer), std::move(inner)); } /////////////////////////////////////////////////////////////////////////////////////////////////// -SkColorFilter* SkColorFilter::CreateComposeFilter(SkColorFilter* outer, SkColorFilter* inner) { +sk_sp SkColorFilter::MakeComposeFilter(sk_sp outer, + sk_sp inner) { if (!outer) { - return SkSafeRef(inner); + return inner; } if (!inner) { - return SkSafeRef(outer); + return outer; } // Give the subclass a shot at a more optimal composition... - SkColorFilter* composition = outer->newComposed(inner); + auto composition = outer->makeComposed(inner); if (composition) { return composition; } @@ -137,7 +169,7 @@ SkColorFilter* SkColorFilter::CreateComposeFilter(SkColorFilter* outer, SkColorF if (count > SK_MAX_COMPOSE_COLORFILTER_COUNT) { return nullptr; } - return new SkComposeColorFilter(outer, inner, count); + return sk_sp(new SkComposeColorFilter(std::move(outer), std::move(inner),count)); } #include "SkModeColorFilter.h" @@ -146,4 +178,3 @@ SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkModeColorFilter) SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END - diff --git a/gfx/skia/skia/src/core/SkColorFilterShader.cpp b/gfx/skia/skia/src/core/SkColorFilterShader.cpp index dc90f95762..500ead4f74 100644 --- a/gfx/skia/skia/src/core/SkColorFilterShader.cpp +++ b/gfx/skia/skia/src/core/SkColorFilterShader.cpp @@ -15,26 +15,26 @@ #include "GrFragmentProcessor.h" #endif -SkColorFilterShader::SkColorFilterShader(SkShader* shader, SkColorFilter* filter) - : fShader(SkRef(shader)) - , fFilter(SkRef(filter)) +SkColorFilterShader::SkColorFilterShader(sk_sp shader, sk_sp filter) + : fShader(std::move(shader)) + , fFilter(std::move(filter)) { - SkASSERT(shader); - SkASSERT(filter); + SkASSERT(fShader); + SkASSERT(fFilter); } -SkFlattenable* SkColorFilterShader::CreateProc(SkReadBuffer& buffer) { - SkAutoTUnref shader(buffer.readShader()); - SkAutoTUnref filter(buffer.readColorFilter()); - if (!shader.get() || !filter.get()) { +sk_sp SkColorFilterShader::CreateProc(SkReadBuffer& buffer) { + auto shader = buffer.readShader(); + auto filter = buffer.readColorFilter(); + if (!shader || !filter) { return nullptr; } - return new SkColorFilterShader(shader, filter); + return sk_make_sp(shader, filter); } void SkColorFilterShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeFlattenable(fShader); - buffer.writeFlattenable(fFilter); + buffer.writeFlattenable(fShader.get()); + buffer.writeFlattenable(fFilter.get()); } uint32_t SkColorFilterShader::FilterShaderContext::getFlags() const { @@ -43,7 +43,9 @@ uint32_t SkColorFilterShader::FilterShaderContext::getFlags() const { uint32_t shaderF = fShaderContext->getFlags(); uint32_t filterF = filterShader.fFilter->getFlags(); - // if the filter might change alpha, clear the opaque flag in the shader + // If the filter does not support a given feature, but sure to clear the corresponding flag + // in the shader flags. + // if (!(filterF & SkColorFilter::kAlphaUnchanged_Flag)) { shaderF &= ~SkShader::kOpaqueAlpha_Flag; } @@ -60,8 +62,8 @@ SkShader::Context* SkColorFilterShader::onCreateContext(const ContextRec& rec, return new (storage) FilterShaderContext(*this, shaderContext, rec); } -size_t SkColorFilterShader::contextSize() const { - return sizeof(FilterShaderContext) + fShader->contextSize(); +size_t SkColorFilterShader::onContextSize(const ContextRec& rec) const { + return sizeof(FilterShaderContext) + fShader->contextSize(rec); } SkColorFilterShader::FilterShaderContext::FilterShaderContext( @@ -84,6 +86,14 @@ void SkColorFilterShader::FilterShaderContext::shadeSpan(int x, int y, SkPMColor filterShader.fFilter->filterSpan(result, count, result); } +void SkColorFilterShader::FilterShaderContext::shadeSpan4f(int x, int y, SkPM4f result[], + int count) { + const SkColorFilterShader& filterShader = static_cast(fShader); + + fShaderContext->shadeSpan4f(x, y, result, count); + filterShader.fFilter->filterSpan4f(result, count, result); +} + #if SK_SUPPORT_GPU ///////////////////////////////////////////////////////////////////// @@ -127,10 +137,10 @@ void SkColorFilterShader::toString(SkString* str) const { /////////////////////////////////////////////////////////////////////////////////////////////////// -SkShader* SkShader::newWithColorFilter(SkColorFilter* filter) const { +sk_sp SkShader::makeWithColorFilter(sk_sp filter) const { SkShader* base = const_cast(this); if (!filter) { - return SkRef(base); + return sk_ref_sp(base); } - return new SkColorFilterShader(base, filter); + return sk_make_sp(sk_ref_sp(base), filter); } diff --git a/gfx/skia/skia/src/core/SkColorFilterShader.h b/gfx/skia/skia/src/core/SkColorFilterShader.h index e92908ee83..e42d06c667 100644 --- a/gfx/skia/skia/src/core/SkColorFilterShader.h +++ b/gfx/skia/skia/src/core/SkColorFilterShader.h @@ -13,10 +13,8 @@ class SkColorFilterShader : public SkShader { public: - SkColorFilterShader(SkShader* shader, SkColorFilter* filter); - - size_t contextSize() const override; - + SkColorFilterShader(sk_sp shader, sk_sp filter); + #if SK_SUPPORT_GPU const GrFragmentProcessor* asFragmentProcessor(GrContext*, const SkMatrix& viewM, @@ -29,34 +27,35 @@ public: // Takes ownership of shaderContext and calls its destructor. FilterShaderContext(const SkColorFilterShader&, SkShader::Context*, const ContextRec&); virtual ~FilterShaderContext(); - + uint32_t getFlags() const override; - + void shadeSpan(int x, int y, SkPMColor[], int count) override; - + void shadeSpan4f(int x, int y, SkPM4f[], int count) override; + void set3DMask(const SkMask* mask) override { // forward to our proxy fShaderContext->set3DMask(mask); } - + private: SkShader::Context* fShaderContext; - + typedef SkShader::Context INHERITED; }; - + SK_TO_STRING_OVERRIDE() SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterShader) - + protected: void flatten(SkWriteBuffer&) const override; + size_t onContextSize(const ContextRec&) const override; Context* onCreateContext(const ContextRec&, void* storage) const override; - - + private: - SkAutoTUnref fShader; - SkAutoTUnref fFilter; - + sk_sp fShader; + sk_sp fFilter; + typedef SkShader INHERITED; }; diff --git a/gfx/skia/skia/src/core/SkColorMatrixFilterRowMajor255.cpp b/gfx/skia/skia/src/core/SkColorMatrixFilterRowMajor255.cpp new file mode 100644 index 0000000000..c158a7948f --- /dev/null +++ b/gfx/skia/skia/src/core/SkColorMatrixFilterRowMajor255.cpp @@ -0,0 +1,437 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkColorMatrixFilterRowMajor255.h" +#include "SkColorPriv.h" +#include "SkNx.h" +#include "SkReadBuffer.h" +#include "SkWriteBuffer.h" +#include "SkUnPreMultiply.h" +#include "SkString.h" +#include "SkPM4fPriv.h" + +static void transpose(float dst[20], const float src[20]) { + const float* srcR = src + 0; + const float* srcG = src + 5; + const float* srcB = src + 10; + const float* srcA = src + 15; + + for (int i = 0; i < 20; i += 4) { + dst[i + 0] = *srcR++; + dst[i + 1] = *srcG++; + dst[i + 2] = *srcB++; + dst[i + 3] = *srcA++; + } +} + +void SkColorMatrixFilterRowMajor255::initState() { + transpose(fTranspose, fMatrix); + + const float* array = fMatrix; + + // check if we have to munge Alpha + bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1) || array[19]); + bool usesAlpha = (array[3] || array[8] || array[13]); + + if (changesAlpha || usesAlpha) { + fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag; + } else { + fFlags = kAlphaUnchanged_Flag; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +SkColorMatrixFilterRowMajor255::SkColorMatrixFilterRowMajor255(const SkScalar array[20]) { + memcpy(fMatrix, array, 20 * sizeof(SkScalar)); + this->initState(); +} + +uint32_t SkColorMatrixFilterRowMajor255::getFlags() const { + return this->INHERITED::getFlags() | fFlags; +} + +static Sk4f scale_rgb(float scale) { + static_assert(SkPM4f::A == 3, "Alpha is lane 3"); + return Sk4f(scale, scale, scale, 1); +} + +static Sk4f premul(const Sk4f& x) { + return x * scale_rgb(x[SkPM4f::A]); +} + +static Sk4f unpremul(const Sk4f& x) { + return x * scale_rgb(1 / x[SkPM4f::A]); // TODO: fast/approx invert? +} + +static Sk4f clamp_0_1(const Sk4f& x) { + return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0)); +} + +static SkPMColor round(const Sk4f& x) { + SkPMColor c; + SkNx_cast(x * Sk4f(255) + Sk4f(0.5f)).store(&c); + return c; +} + +template +void filter_span(const float array[], const T src[], int count, T dst[]) { + // c0-c3 are already in [0,1]. + const Sk4f c0 = Sk4f::Load(array + 0); + const Sk4f c1 = Sk4f::Load(array + 4); + const Sk4f c2 = Sk4f::Load(array + 8); + const Sk4f c3 = Sk4f::Load(array + 12); + // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1]. + const Sk4f c4 = Sk4f::Load(array + 16)*Sk4f(1.0f/255); + + // todo: we could cache this in the constructor... + T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4))); + + for (int i = 0; i < count; i++) { + Sk4f srcf = Adaptor::To4f(src[i]); + float srcA = srcf[SkPM4f::A]; + + if (0 == srcA) { + dst[i] = matrix_translate_pmcolor; + continue; + } + if (1 != srcA) { + srcf = unpremul(srcf); + } + + Sk4f r4 = srcf[Adaptor::R]; + Sk4f g4 = srcf[Adaptor::G]; + Sk4f b4 = srcf[Adaptor::B]; + Sk4f a4 = srcf[Adaptor::A]; + // apply matrix + Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4; + + dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4))); + } +} + +struct SkPMColorAdaptor { + enum { + R = SK_R_INDEX, + G = SK_G_INDEX, + B = SK_B_INDEX, + A = SK_A_INDEX, + }; + static SkPMColor From4f(const Sk4f& c4) { + return round(swizzle_rb_if_bgra(c4)); + } + static Sk4f To4f(SkPMColor c) { + return to_4f(c) * Sk4f(1.0f/255); + } +}; +void SkColorMatrixFilterRowMajor255::filterSpan(const SkPMColor src[], int count, SkPMColor dst[]) const { + filter_span(fTranspose, src, count, dst); +} + +struct SkPM4fAdaptor { + enum { + R = SkPM4f::R, + G = SkPM4f::G, + B = SkPM4f::B, + A = SkPM4f::A, + }; + static SkPM4f From4f(const Sk4f& c4) { + return SkPM4f::From4f(c4); + } + static Sk4f To4f(const SkPM4f& c) { + return c.to4f(); + } +}; +void SkColorMatrixFilterRowMajor255::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst[]) const { + filter_span(fTranspose, src, count, dst); +} + +/////////////////////////////////////////////////////////////////////////////// + +void SkColorMatrixFilterRowMajor255::flatten(SkWriteBuffer& buffer) const { + SkASSERT(sizeof(fMatrix)/sizeof(SkScalar) == 20); + buffer.writeScalarArray(fMatrix, 20); +} + +sk_sp SkColorMatrixFilterRowMajor255::CreateProc(SkReadBuffer& buffer) { + SkScalar matrix[20]; + if (buffer.readScalarArray(matrix, 20)) { + return sk_make_sp(matrix); + } + return nullptr; +} + +bool SkColorMatrixFilterRowMajor255::asColorMatrix(SkScalar matrix[20]) const { + if (matrix) { + memcpy(matrix, fMatrix, 20 * sizeof(SkScalar)); + } + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// This code was duplicated from src/effects/SkColorMatrixc.cpp in order to be used in core. +////// + +// To detect if we need to apply clamping after applying a matrix, we check if +// any output component might go outside of [0, 255] for any combination of +// input components in [0..255]. +// Each output component is an affine transformation of the input component, so +// the minimum and maximum values are for any combination of minimum or maximum +// values of input components (i.e. 0 or 255). +// E.g. if R' = x*R + y*G + z*B + w*A + t +// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the +// minimum value will be for R=0 if x>0 or R=255 if x<0. +// Same goes for all components. +static bool component_needs_clamping(const SkScalar row[5]) { + SkScalar maxValue = row[4] / 255; + SkScalar minValue = row[4] / 255; + for (int i = 0; i < 4; ++i) { + if (row[i] > 0) + maxValue += row[i]; + else + minValue += row[i]; + } + return (maxValue > 1) || (minValue < 0); +} + +static bool needs_clamping(const SkScalar matrix[20]) { + return component_needs_clamping(matrix) + || component_needs_clamping(matrix+5) + || component_needs_clamping(matrix+10) + || component_needs_clamping(matrix+15); +} + +static void set_concat(SkScalar result[20], const SkScalar outer[20], const SkScalar inner[20]) { + int index = 0; + for (int j = 0; j < 20; j += 5) { + for (int i = 0; i < 4; i++) { + result[index++] = outer[j + 0] * inner[i + 0] + + outer[j + 1] * inner[i + 5] + + outer[j + 2] * inner[i + 10] + + outer[j + 3] * inner[i + 15]; + } + result[index++] = outer[j + 0] * inner[4] + + outer[j + 1] * inner[9] + + outer[j + 2] * inner[14] + + outer[j + 3] * inner[19] + + outer[j + 4]; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// End duplication +////// + +sk_sp +SkColorMatrixFilterRowMajor255::makeComposed(sk_sp innerFilter) const { + SkScalar innerMatrix[20]; + if (innerFilter->asColorMatrix(innerMatrix) && !needs_clamping(innerMatrix)) { + SkScalar concat[20]; + set_concat(concat, fMatrix, innerMatrix); + return sk_make_sp(concat); + } + return nullptr; +} + +#if SK_SUPPORT_GPU +#include "GrFragmentProcessor.h" +#include "GrInvariantOutput.h" +#include "glsl/GrGLSLFragmentProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" +#include "glsl/GrGLSLProgramDataManager.h" +#include "glsl/GrGLSLUniformHandler.h" + +class ColorMatrixEffect : public GrFragmentProcessor { +public: + static const GrFragmentProcessor* Create(const SkScalar matrix[20]) { + return new ColorMatrixEffect(matrix); + } + + const char* name() const override { return "Color Matrix"; } + + GR_DECLARE_FRAGMENT_PROCESSOR_TEST; + + class GLSLProcessor : public GrGLSLFragmentProcessor { + public: + // this class always generates the same code. + static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKeyBuilder*) {} + + void emitCode(EmitArgs& args) override { + GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; + fMatrixHandle = uniformHandler->addUniform(kFragment_GrShaderFlag, + kMat44f_GrSLType, kDefault_GrSLPrecision, + "ColorMatrix"); + fVectorHandle = uniformHandler->addUniform(kFragment_GrShaderFlag, + kVec4f_GrSLType, kDefault_GrSLPrecision, + "ColorMatrixVector"); + + if (nullptr == args.fInputColor) { + // could optimize this case, but we aren't for now. + args.fInputColor = "vec4(1)"; + } + GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; + // The max() is to guard against 0 / 0 during unpremul when the incoming color is + // transparent black. + fragBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", + args.fInputColor); + fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n", + args.fOutputColor, + uniformHandler->getUniformCStr(fMatrixHandle), + args.fInputColor, + uniformHandler->getUniformCStr(fVectorHandle)); + fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n", + args.fOutputColor, args.fOutputColor); + fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, args.fOutputColor); + } + + protected: + void onSetData(const GrGLSLProgramDataManager& uniManager, + const GrProcessor& proc) override { + const ColorMatrixEffect& cme = proc.cast(); + const float* m = cme.fMatrix; + // The GL matrix is transposed from SkColorMatrix. + float mt[] = { + m[0], m[5], m[10], m[15], + m[1], m[6], m[11], m[16], + m[2], m[7], m[12], m[17], + m[3], m[8], m[13], m[18], + }; + static const float kScale = 1.0f / 255.0f; + float vec[] = { + m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale, + }; + uniManager.setMatrix4fv(fMatrixHandle, 1, mt); + uniManager.set4fv(fVectorHandle, 1, vec); + } + + private: + GrGLSLProgramDataManager::UniformHandle fMatrixHandle; + GrGLSLProgramDataManager::UniformHandle fVectorHandle; + + typedef GrGLSLFragmentProcessor INHERITED; + }; + +private: + ColorMatrixEffect(const SkScalar matrix[20]) { + memcpy(fMatrix, matrix, sizeof(SkScalar) * 20); + this->initClassID(); + } + + GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { + return new GLSLProcessor; + } + + virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps, + GrProcessorKeyBuilder* b) const override { + GLSLProcessor::GenKey(*this, caps, b); + } + + bool onIsEqual(const GrFragmentProcessor& s) const override { + const ColorMatrixEffect& cme = s.cast(); + return 0 == memcmp(fMatrix, cme.fMatrix, sizeof(fMatrix)); + } + + void onComputeInvariantOutput(GrInvariantOutput* inout) const override { + // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had + // type flags it might be worth checking the other components. + + // The matrix is defined such the 4th row determines the output alpha. The first four + // columns of that row multiply the input r, g, b, and a, respectively, and the last column + // is the "translation". + static const uint32_t kRGBAFlags[] = { + kR_GrColorComponentFlag, + kG_GrColorComponentFlag, + kB_GrColorComponentFlag, + kA_GrColorComponentFlag + }; + static const int kShifts[] = { + GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A, + }; + enum { + kAlphaRowStartIdx = 15, + kAlphaRowTranslateIdx = 19, + }; + + SkScalar outputA = 0; + for (int i = 0; i < 4; ++i) { + // If any relevant component of the color to be passed through the matrix is non-const + // then we can't know the final result. + if (0 != fMatrix[kAlphaRowStartIdx + i]) { + if (!(inout->validFlags() & kRGBAFlags[i])) { + inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); + return; + } else { + uint32_t component = (inout->color() >> kShifts[i]) & 0xFF; + outputA += fMatrix[kAlphaRowStartIdx + i] * component; + } + } + } + outputA += fMatrix[kAlphaRowTranslateIdx]; + // We pin the color to [0,1]. This would happen to the *final* color output from the frag + // shader but currently the effect does not pin its own output. So in the case of over/ + // underflow this may deviate from the actual result. Maybe the effect should pin its + // result if the matrix could over/underflow for any component? + inout->setToOther(kA_GrColorComponentFlag, + static_cast(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A, + GrInvariantOutput::kWill_ReadInput); + } + + SkScalar fMatrix[20]; + + typedef GrFragmentProcessor INHERITED; +}; + +GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect); + +const GrFragmentProcessor* ColorMatrixEffect::TestCreate(GrProcessorTestData* d) { + SkScalar colorMatrix[20]; + for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix); ++i) { + colorMatrix[i] = d->fRandom->nextSScalar1(); + } + return ColorMatrixEffect::Create(colorMatrix); +} + +const GrFragmentProcessor* SkColorMatrixFilterRowMajor255::asFragmentProcessor(GrContext*) const { + return ColorMatrixEffect::Create(fMatrix); +} + +#endif + +#ifndef SK_IGNORE_TO_STRING +void SkColorMatrixFilterRowMajor255::toString(SkString* str) const { + str->append("SkColorMatrixFilterRowMajor255: "); + + str->append("matrix: ("); + for (int i = 0; i < 20; ++i) { + str->appendScalar(fMatrix[i]); + if (i < 19) { + str->append(", "); + } + } + str->append(")"); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// + +sk_sp SkColorFilter::MakeMatrixFilterRowMajor255(const SkScalar array[20]) { + return sk_sp(new SkColorMatrixFilterRowMajor255(array)); +} + +/////////////////////////////////////////////////////////////////////////////// + +sk_sp +SkColorMatrixFilterRowMajor255::MakeSingleChannelOutput(const SkScalar row[5]) { + SkASSERT(row); + auto cf = sk_make_sp(); + static_assert(sizeof(SkScalar) * 5 * 4 == sizeof(cf->fMatrix), "sizes don't match"); + for (int i = 0; i < 4; ++i) { + memcpy(cf->fMatrix + 5 * i, row, sizeof(SkScalar) * 5); + } + cf->initState(); + return cf; +} diff --git a/gfx/skia/skia/src/core/SkColorMatrixFilterRowMajor255.h b/gfx/skia/skia/src/core/SkColorMatrixFilterRowMajor255.h new file mode 100644 index 0000000000..0ad64fa2f0 --- /dev/null +++ b/gfx/skia/skia/src/core/SkColorMatrixFilterRowMajor255.h @@ -0,0 +1,48 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorMatrixFilter_DEFINED +#define SkColorMatrixFilter_DEFINED + +#include "SkColorFilter.h" + +class SK_API SkColorMatrixFilterRowMajor255 : public SkColorFilter { +public: + SkColorMatrixFilterRowMajor255() {}; + explicit SkColorMatrixFilterRowMajor255(const SkScalar array[20]); + + /** Creates a color matrix filter that returns the same value in all four channels. */ + static sk_sp MakeSingleChannelOutput(const SkScalar row[5]); + + void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const override; + void filterSpan4f(const SkPM4f src[], int count, SkPM4f[]) const override; + uint32_t getFlags() const override; + bool asColorMatrix(SkScalar matrix[20]) const override; + sk_sp makeComposed(sk_sp) const override; + +#if SK_SUPPORT_GPU + const GrFragmentProcessor* asFragmentProcessor(GrContext*) const override; +#endif + + SK_TO_STRING_OVERRIDE() + + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorMatrixFilter) + +protected: + void flatten(SkWriteBuffer&) const override; + +private: + SkScalar fMatrix[20]; + float fTranspose[20]; // for Sk4s + uint32_t fFlags; + + void initState(); + + typedef SkColorFilter INHERITED; +}; + +#endif diff --git a/gfx/skia/skia/src/core/SkColorShader.h b/gfx/skia/skia/src/core/SkColorShader.h index 25a1d6c8d0..db71b6ef9f 100644 --- a/gfx/skia/skia/src/core/SkColorShader.h +++ b/gfx/skia/skia/src/core/SkColorShader.h @@ -9,6 +9,7 @@ #define SkColorShader_DEFINED #include "SkShader.h" +#include "SkPM4f.h" /** \class SkColorShader A Shader that represents a single color. In general, this effect can be @@ -25,10 +26,6 @@ public: bool isOpaque() const override; - size_t contextSize() const override { - return sizeof(ColorShaderContext); - } - class ColorShaderContext : public SkShader::Context { public: ColorShaderContext(const SkColorShader& shader, const ContextRec&); @@ -36,8 +33,13 @@ public: uint32_t getFlags() const override; void shadeSpan(int x, int y, SkPMColor span[], int count) override; void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) override; + void shadeSpan4f(int x, int y, SkPM4f[], int count) override; + + protected: + bool onChooseBlitProcs(const SkImageInfo&, BlitState*) override; private: + SkPM4f fPM4f; SkPMColor fPMColor; uint32_t fFlags; @@ -58,6 +60,7 @@ protected: SkColorShader(SkReadBuffer&); void flatten(SkWriteBuffer&) const override; Context* onCreateContext(const ContextRec&, void* storage) const override; + size_t onContextSize(const ContextRec&) const override { return sizeof(ColorShaderContext); } bool onAsLuminanceColor(SkColor* lum) const override { *lum = fColor; return true; diff --git a/gfx/skia/skia/src/core/SkColorSpace.cpp b/gfx/skia/skia/src/core/SkColorSpace.cpp new file mode 100644 index 0000000000..5499469651 --- /dev/null +++ b/gfx/skia/skia/src/core/SkColorSpace.cpp @@ -0,0 +1,550 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkAtomics.h" +#include "SkColorSpace.h" + +static inline bool SkFloatIsFinite(float x) { return 0 == x * 0; } + +// +// SkFloat3x3 +// +// In memory order, values are a, b, c, d, e, f, g, h, i +// +// When applied to a color component vector (e.g. [ r, r, r ] or [ g, g, g ] we do +// +// [ r r r ] * [ a b c ] + [ g g g ] * [ d e f ] + [ b b b ] * [ g h i ] +// +// Thus in our point-on-the-right notation, the matrix looks like +// +// [ a d g ] [ r ] +// [ b e h ] * [ g ] +// [ c f i ] [ b ] +// +static SkFloat3x3 concat(const SkFloat3x3& left, const SkFloat3x3& rite) { + SkFloat3x3 result; + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 3; ++col) { + double tmp = 0; + for (int i = 0; i < 3; ++i) { + tmp += (double)left.fMat[row + i * 3] * rite.fMat[i + col * 3]; + } + result.fMat[row + col * 3] = (double)tmp; + } + } + return result; +} + +static double det(const SkFloat3x3& m) { + return (double)m.fMat[0] * m.fMat[4] * m.fMat[8] + + (double)m.fMat[3] * m.fMat[7] * m.fMat[2] + + (double)m.fMat[6] * m.fMat[1] * m.fMat[5] - + (double)m.fMat[0] * m.fMat[7] * m.fMat[5] - + (double)m.fMat[3] * m.fMat[1] * m.fMat[8] - + (double)m.fMat[6] * m.fMat[4] * m.fMat[2]; +} + +static double det2x2(const SkFloat3x3& m, int a, int b, int c, int d) { + return (double)m.fMat[a] * m.fMat[b] - (double)m.fMat[c] * m.fMat[d]; +} + +static SkFloat3x3 invert(const SkFloat3x3& m) { + double d = det(m); + SkASSERT(SkFloatIsFinite((float)d)); + double scale = 1 / d; + SkASSERT(SkFloatIsFinite((float)scale)); + + return {{ + (float)(scale * det2x2(m, 4, 8, 5, 7)), + (float)(scale * det2x2(m, 7, 2, 8, 1)), + (float)(scale * det2x2(m, 1, 5, 2, 4)), + + (float)(scale * det2x2(m, 6, 5, 8, 3)), + (float)(scale * det2x2(m, 0, 8, 2, 6)), + (float)(scale * det2x2(m, 3, 2, 5, 0)), + + (float)(scale * det2x2(m, 3, 7, 4, 6)), + (float)(scale * det2x2(m, 6, 1, 7, 0)), + (float)(scale * det2x2(m, 0, 4, 1, 3)), + }}; +} + +void SkFloat3::dump() const { + SkDebugf("[%7.4f %7.4f %7.4f]\n", fVec[0], fVec[1], fVec[2]); +} + +void SkFloat3x3::dump() const { + SkDebugf("[%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f] [%7.4f %7.4f %7.4f]\n", + fMat[0], fMat[1], fMat[2], + fMat[3], fMat[4], fMat[5], + fMat[6], fMat[7], fMat[8]); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +static int32_t gUniqueColorSpaceID; + +SkColorSpace::SkColorSpace(const SkFloat3x3& toXYZD50, const SkFloat3& gamma, Named named) + : fToXYZD50(toXYZD50) + , fGamma(gamma) + , fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID)) + , fNamed(named) +{ + for (int i = 0; i < 3; ++i) { + SkASSERT(SkFloatIsFinite(gamma.fVec[i])); + for (int j = 0; j < 3; ++j) { + SkASSERT(SkFloatIsFinite(toXYZD50.fMat[3*i + j])); + } + } +} + +sk_sp SkColorSpace::NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& gamma) { + for (int i = 0; i < 3; ++i) { + if (!SkFloatIsFinite(gamma.fVec[i]) || gamma.fVec[i] < 0) { + return nullptr; + } + for (int j = 0; j < 3; ++j) { + if (!SkFloatIsFinite(toXYZD50.fMat[3*i + j])) { + return nullptr; + } + } + } + + // check the matrix for invertibility + float d = det(toXYZD50); + if (!SkFloatIsFinite(d) || !SkFloatIsFinite(1 / d)) { + return nullptr; + } + + return sk_sp(new SkColorSpace(toXYZD50, gamma, kUnknown_Named)); +} + +void SkColorSpace::dump() const { + fToXYZD50.dump(); + fGamma.dump(); +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + +const SkFloat3 gDevice_gamma {{ 0, 0, 0 }}; +const SkFloat3x3 gDevice_toXYZD50 {{ + 1, 0, 0, + 0, 1, 0, + 0, 0, 1 +}}; + +const SkFloat3 gSRGB_gamma {{ 2.2f, 2.2f, 2.2f }}; +const SkFloat3x3 gSRGB_toXYZD50 {{ + 0.4358f, 0.2224f, 0.0139f, // * R + 0.3853f, 0.7170f, 0.0971f, // * G + 0.1430f, 0.0606f, 0.7139f, // * B +}}; + +sk_sp SkColorSpace::NewNamed(Named named) { + switch (named) { + case kDevice_Named: + return sk_sp(new SkColorSpace(gDevice_toXYZD50, gDevice_gamma, + kDevice_Named)); + case kSRGB_Named: + return sk_sp(new SkColorSpace(gSRGB_toXYZD50, gSRGB_gamma, kSRGB_Named)); + default: + break; + } + return nullptr; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#include "SkFixed.h" +#include "SkTemplates.h" + +#define SkColorSpacePrintf(...) + +#define return_if_false(pred, msg) \ + do { \ + if (!(pred)) { \ + SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ + return false; \ + } \ + } while (0) + +#define return_null(msg) \ + do { \ + SkColorSpacePrintf("Invalid ICC Profile: %s.\n", (msg)); \ + return nullptr; \ + } while (0) + +static uint16_t read_big_endian_short(const uint8_t* ptr) { + return ptr[0] << 8 | ptr[1]; +} + +static uint32_t read_big_endian_int(const uint8_t* ptr) { + return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; +} + +// This is equal to the header size according to the ICC specification (128) +// plus the size of the tag count (4). We include the tag count since we +// always require it to be present anyway. +static const size_t kICCHeaderSize = 132; + +// Contains a signature (4), offset (4), and size (4). +static const size_t kICCTagTableEntrySize = 12; + +static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); +static const uint32_t kGray_ColorSpace = SkSetFourByteTag('G', 'R', 'A', 'Y'); + +struct ICCProfileHeader { + // TODO (msarett): + // Can we ignore less of these fields? + uint32_t fSize; + uint32_t fCMMType_ignored; + uint32_t fVersion; + uint32_t fClassProfile; + uint32_t fColorSpace; + uint32_t fPCS; + uint32_t fDateTime_ignored[3]; + uint32_t fSignature; + uint32_t fPlatformTarget_ignored; + uint32_t fFlags_ignored; + uint32_t fManufacturer_ignored; + uint32_t fDeviceModel_ignored; + uint32_t fDeviceAttributes_ignored[2]; + uint32_t fRenderingIntent; + uint32_t fIlluminantXYZ_ignored[3]; + uint32_t fCreator_ignored; + uint32_t fProfileId_ignored[4]; + uint32_t fReserved_ignored[7]; + uint32_t fTagCount; + + void init(const uint8_t* src, size_t len) { + SkASSERT(kICCHeaderSize == sizeof(*this)); + + uint32_t* dst = (uint32_t*) this; + for (uint32_t i = 0; i < kICCHeaderSize / 4; i++, src+=4) { + dst[i] = read_big_endian_int(src); + } + } + + bool valid() const { + // TODO (msarett): + // For now it's nice to fail loudly on invalid inputs. But, can we + // recover from some of these errors? + + return_if_false(fSize >= kICCHeaderSize, "Size is too small"); + + uint8_t majorVersion = fVersion >> 24; + return_if_false(majorVersion <= 4, "Unsupported version"); + + const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); + const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); + const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); + // TODO (msarett): + // Should we also support DeviceLink, ColorSpace, Abstract, or NamedColor? + return_if_false(fClassProfile == kDisplay_Profile || + fClassProfile == kInput_Profile || + fClassProfile == kOutput_Profile, + "Unsupported class profile"); + + // TODO (msarett): + // There are many more color spaces that we could try to support. + return_if_false(fColorSpace == kRGB_ColorSpace || fColorSpace == kGray_ColorSpace, + "Unsupported color space"); + + const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); + // TODO (msarett): + // Can we support PCS LAB as well? + return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); + + return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); + + // TODO (msarett): + // Should we treat different rendering intents differently? + // Valid rendering intents include kPerceptual (0), kRelative (1), + // kSaturation (2), and kAbsolute (3). + return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); + + return_if_false(fTagCount <= 100, "Too many tags"); + + return true; + } +}; + +struct ICCTag { + uint32_t fSignature; + uint32_t fOffset; + uint32_t fLength; + + const uint8_t* init(const uint8_t* src) { + fSignature = read_big_endian_int(src); + fOffset = read_big_endian_int(src + 4); + fLength = read_big_endian_int(src + 8); + return src + 12; + } + + bool valid(size_t len) { + return_if_false(fOffset + fLength <= len, "Tag too large for ICC profile"); + return true; + } + + const uint8_t* addr(const uint8_t* src) const { + return src + fOffset; + } + + static const ICCTag* Find(const ICCTag tags[], int count, uint32_t signature) { + for (int i = 0; i < count; ++i) { + if (tags[i].fSignature == signature) { + return &tags[i]; + } + } + return nullptr; + } +}; + +// TODO (msarett): +// Should we recognize more tags? +static const uint32_t kTAG_rXYZ = SkSetFourByteTag('r', 'X', 'Y', 'Z'); +static const uint32_t kTAG_gXYZ = SkSetFourByteTag('g', 'X', 'Y', 'Z'); +static const uint32_t kTAG_bXYZ = SkSetFourByteTag('b', 'X', 'Y', 'Z'); +static const uint32_t kTAG_rTRC = SkSetFourByteTag('r', 'T', 'R', 'C'); +static const uint32_t kTAG_gTRC = SkSetFourByteTag('g', 'T', 'R', 'C'); +static const uint32_t kTAG_bTRC = SkSetFourByteTag('b', 'T', 'R', 'C'); + +bool load_xyz(float dst[3], const uint8_t* src, size_t len) { + if (len < 20) { + SkColorSpacePrintf("XYZ tag is too small (%d bytes)", len); + return false; + } + + dst[0] = SkFixedToFloat(read_big_endian_int(src + 8)); + dst[1] = SkFixedToFloat(read_big_endian_int(src + 12)); + dst[2] = SkFixedToFloat(read_big_endian_int(src + 16)); + SkColorSpacePrintf("XYZ %g %g %g\n", dst[0], dst[1], dst[2]); + return true; +} + +static const uint32_t kTAG_CurveType = SkSetFourByteTag('c', 'u', 'r', 'v'); +static const uint32_t kTAG_ParaCurveType = SkSetFourByteTag('p', 'a', 'r', 'a'); + +static bool load_gamma(float* gamma, const uint8_t* src, size_t len) { + if (len < 14) { + SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); + return false; + } + + uint32_t type = read_big_endian_int(src); + switch (type) { + case kTAG_CurveType: { + uint32_t count = read_big_endian_int(src + 8); + if (0 == count) { + return false; + } + + const uint16_t* table = (const uint16_t*) (src + 12); + if (1 == count) { + // Table entry is the exponent (bias 256). + uint16_t value = read_big_endian_short((const uint8_t*) table); + *gamma = value / 256.0f; + SkColorSpacePrintf("gamma %d %g\n", value, *gamma); + return true; + } + + // Check length again if we have a table. + if (len < 12 + 2 * count) { + SkColorSpacePrintf("gamma tag is too small (%d bytes)", len); + return false; + } + + // Print the interpolation table. For now, we ignore this and guess 2.2f. + for (uint32_t i = 0; i < count; i++) { + SkColorSpacePrintf("curve[%d] %d\n", i, + read_big_endian_short((const uint8_t*) &table[i])); + } + + *gamma = 2.2f; + return true; + } + case kTAG_ParaCurveType: + // Guess 2.2f. + SkColorSpacePrintf("parametric curve\n"); + *gamma = 2.2f; + return true; + default: + SkColorSpacePrintf("Unsupported gamma tag type %d\n", type); + return false; + } +} + +sk_sp SkColorSpace::NewICC(const void* base, size_t len) { + const uint8_t* ptr = (const uint8_t*) base; + + if (len < kICCHeaderSize) { + return_null("Data is not large enough to contain an ICC profile"); + } + + // Read the ICC profile header and check to make sure that it is valid. + ICCProfileHeader header; + header.init(ptr, len); + if (!header.valid()) { + return nullptr; + } + + // Adjust ptr and len before reading the tags. + if (len < header.fSize) { + SkColorSpacePrintf("ICC profile might be truncated.\n"); + } else if (len > header.fSize) { + SkColorSpacePrintf("Caller provided extra data beyond the end of the ICC profile.\n"); + len = header.fSize; + } + ptr += kICCHeaderSize; + len -= kICCHeaderSize; + + // Parse tag headers. + uint32_t tagCount = header.fTagCount; + SkColorSpacePrintf("ICC profile contains %d tags.\n", tagCount); + if (len < kICCTagTableEntrySize * tagCount) { + return_null("Not enough input data to read tag table entries"); + } + + SkAutoTArray tags(tagCount); + for (uint32_t i = 0; i < tagCount; i++) { + ptr = tags[i].init(ptr); + SkColorSpacePrintf("[%d] %c%c%c%c %d %d\n", i, (tags[i].fSignature >> 24) & 0xFF, + (tags[i].fSignature >> 16) & 0xFF, (tags[i].fSignature >> 8) & 0xFF, + (tags[i].fSignature >> 0) & 0xFF, tags[i].fOffset, tags[i].fLength); + + if (!tags[i].valid(kICCHeaderSize + len)) { + return_null("Tag is too large to fit in ICC profile"); + } + } + + // Load our XYZ and gamma matrices. + SkFloat3x3 toXYZ; + SkFloat3 gamma {{ 1.0f, 1.0f, 1.0f }}; + switch (header.fColorSpace) { + case kRGB_ColorSpace: { + const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ); + const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ); + const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ); + if (!r || !g || !b) { + return_null("Need rgb tags for XYZ space"); + } + + if (!load_xyz(&toXYZ.fMat[0], r->addr((const uint8_t*) base), r->fLength) || + !load_xyz(&toXYZ.fMat[3], g->addr((const uint8_t*) base), g->fLength) || + !load_xyz(&toXYZ.fMat[6], b->addr((const uint8_t*) base), b->fLength)) + { + return_null("Need valid rgb tags for XYZ space"); + } + + r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC); + g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC); + b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC); + if (!r || !load_gamma(&gamma.fVec[0], r->addr((const uint8_t*) base), r->fLength)) { + SkColorSpacePrintf("Failed to read R gamma tag.\n"); + } + if (!g || !load_gamma(&gamma.fVec[1], g->addr((const uint8_t*) base), g->fLength)) { + SkColorSpacePrintf("Failed to read G gamma tag.\n"); + } + if (!b || !load_gamma(&gamma.fVec[2], b->addr((const uint8_t*) base), b->fLength)) { + SkColorSpacePrintf("Failed to read B gamma tag.\n"); + } + return SkColorSpace::NewRGB(toXYZ, gamma); + } + default: + break; + } + + return_null("ICC profile contains unsupported colorspace"); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SkColorSpace::Result SkColorSpace::Concat(const SkColorSpace* src, const SkColorSpace* dst, + SkFloat3x3* result) { + if (!src || !dst || (src->named() == kDevice_Named) || (src->named() == dst->named())) { + if (result) { + *result = {{ 1, 0, 0, 0, 1, 0, 0, 0, 1 }}; + } + return kIdentity_Result; + } + if (result) { + *result = concat(src->fToXYZD50, invert(dst->fToXYZD50)); + } + return kNormal_Result; +} + +#include "SkColor.h" +#include "SkNx.h" +#include "SkPM4f.h" + +void SkApply3x3ToPM4f(const SkFloat3x3& m, const SkPM4f src[], SkPM4f dst[], int count) { + SkASSERT(1 == SkPM4f::G); + SkASSERT(3 == SkPM4f::A); + + Sk4f cr, cg, cb; + cg = Sk4f::Load(m.fMat + 3); + if (0 == SkPM4f::R) { + SkASSERT(2 == SkPM4f::B); + cr = Sk4f::Load(m.fMat + 0); + cb = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); + } else { + SkASSERT(0 == SkPM4f::B); + SkASSERT(2 == SkPM4f::R); + cb = Sk4f::Load(m.fMat + 0); + cr = Sk4f(m.fMat[6], m.fMat[7], m.fMat[8], 0); + } + cr = cr * Sk4f(1, 1, 1, 0); + cg = cg * Sk4f(1, 1, 1, 0); + cb = cb * Sk4f(1, 1, 1, 0); + + for (int i = 0; i < count; ++i) { + Sk4f r = Sk4f(src[i].fVec[SkPM4f::R]); + Sk4f g = Sk4f(src[i].fVec[SkPM4f::G]); + Sk4f b = Sk4f(src[i].fVec[SkPM4f::B]); + Sk4f a = Sk4f(0, 0, 0, src[i].fVec[SkPM4f::A]); + (cr * r + cg * g + cb * b + a).store(&dst[i]); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +void SkColorSpace::Test() { + SkFloat3x3 mat {{ 2, 0, 0, 0, 3, 0, 0, 0, 4 }}; + SkFloat3x3 inv = invert(mat); + mat.dump(); + inv.dump(); + concat(mat, inv).dump(); + concat(inv, mat).dump(); + SkDebugf("\n"); + + mat = gSRGB_toXYZD50; + inv = invert(mat); + mat.dump(); + inv.dump(); + concat(mat, inv).dump(); + concat(inv, mat).dump(); + SkDebugf("\n"); + + sk_sp cs0(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); + sk_sp cs1(SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)); + + cs0->dump(); + cs1->dump(); + SkFloat3x3 xform; + (void)SkColorSpace::Concat(cs0.get(), cs1.get(), &xform); + xform.dump(); + SkDebugf("\n"); +} + +// D65 white point of Rec. 709 [8] are: +// +// D65 white-point in unit luminance XYZ = 0.9505, 1.0000, 1.0890 +// +// R G B white +// x 0.640 0.300 0.150 0.3127 +// y 0.330 0.600 0.060 0.3290 +// z 0.030 0.100 0.790 0.3582 diff --git a/gfx/skia/skia/src/core/SkColorSpace.h b/gfx/skia/skia/src/core/SkColorSpace.h new file mode 100644 index 0000000000..777be9da2b --- /dev/null +++ b/gfx/skia/skia/src/core/SkColorSpace.h @@ -0,0 +1,88 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkColorSpace_DEFINED +#define SkColorSpace_DEFINED + +// Some terms +// +// PCS : Profile Connection Space : where color number values have an absolute meaning. +// Part of the work float is to convert colors to and from this space... +// src_linear_unit_floats --> PCS --> PCS' --> dst_linear_unit_floats +// +// Some nice documents +// +// http://www.cambridgeincolour.com/tutorials/color-space-conversion.htm +// https://www.w3.org/Graphics/Color/srgb +// http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html +// + +#include "SkRefCnt.h" + +struct SkFloat3 { + float fVec[3]; + + void dump() const; +}; + +struct SkFloat3x3 { + float fMat[9]; + + void dump() const; +}; + +struct SkPM4f; +void SkApply3x3ToPM4f(const SkFloat3x3&, const SkPM4f src[], SkPM4f dst[], int count); + +class SkColorSpace : public SkRefCnt { +public: + enum Named { + kUnknown_Named, + kDevice_Named, + kSRGB_Named, + }; + + /** + * Return a colorspace instance, given a 3x3 transform from linear_RGB to D50_XYZ + * and the src-gamma, return a ColorSpace + */ + static sk_sp NewRGB(const SkFloat3x3& toXYZD50, const SkFloat3& gamma); + + static sk_sp NewNamed(Named); + static sk_sp NewICC(const void*, size_t); + + SkFloat3 gamma() const { return fGamma; } + SkFloat3x3 xyz() const { return fToXYZD50; } + Named named() const { return fNamed; } + uint32_t uniqueID() const { return fUniqueID; } + + enum Result { + kFailure_Result, + kIdentity_Result, + kNormal_Result, + }; + + /** + * Given a src and dst colorspace, return the 3x3 matrix that will convert src_linear_RGB + * values into dst_linear_RGB values. + */ + static Result Concat(const SkColorSpace* src, const SkColorSpace* dst, SkFloat3x3* result); + + static void Test(); + void dump() const; + +protected: + SkColorSpace(const SkFloat3x3& toXYZ, const SkFloat3& gamma, Named); + +private: + const SkFloat3x3 fToXYZD50; + const SkFloat3 fGamma; + const uint32_t fUniqueID; + const Named fNamed; +}; + +#endif diff --git a/gfx/skia/skia/src/core/SkColorTable.cpp b/gfx/skia/skia/src/core/SkColorTable.cpp index 8e889d44c4..d7253e1d8c 100644 --- a/gfx/skia/skia/src/core/SkColorTable.cpp +++ b/gfx/skia/skia/src/core/SkColorTable.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2009 The Android Open Source Project * @@ -107,6 +106,5 @@ SkColorTable* SkColorTable::Create(SkReadBuffer& buffer) { return nullptr; } - return new SkColorTable(colors.detach(), count, kAllocatedWithMalloc); + return new SkColorTable(colors.release(), count, kAllocatedWithMalloc); } - diff --git a/gfx/skia/skia/src/core/SkComposeShader.cpp b/gfx/skia/skia/src/core/SkComposeShader.cpp index d433ff2d43..46b7724944 100644 --- a/gfx/skia/skia/src/core/SkComposeShader.cpp +++ b/gfx/skia/skia/src/core/SkComposeShader.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,7 +5,6 @@ * found in the LICENSE file. */ - #include "SkComposeShader.h" #include "SkColorFilter.h" #include "SkColorPriv.h" @@ -18,22 +16,10 @@ /////////////////////////////////////////////////////////////////////////////// -SkComposeShader::SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode) { - fShaderA = sA; sA->ref(); - fShaderB = sB; sB->ref(); - // mode may be null - fMode = mode; - SkSafeRef(mode); -} - -SkComposeShader::~SkComposeShader() { - SkSafeUnref(fMode); - fShaderB->unref(); - fShaderA->unref(); -} - -size_t SkComposeShader::contextSize() const { - return sizeof(ComposeShaderContext) + fShaderA->contextSize() + fShaderB->contextSize(); +size_t SkComposeShader::onContextSize(const ContextRec& rec) const { + return sizeof(ComposeShaderContext) + + fShaderA->contextSize(rec) + + fShaderB->contextSize(rec); } class SkAutoAlphaRestore { @@ -53,20 +39,20 @@ private: }; #define SkAutoAlphaRestore(...) SK_REQUIRE_LOCAL_VAR(SkAutoAlphaRestore) -SkFlattenable* SkComposeShader::CreateProc(SkReadBuffer& buffer) { - SkAutoTUnref shaderA(buffer.readShader()); - SkAutoTUnref shaderB(buffer.readShader()); - SkAutoTUnref mode(buffer.readXfermode()); - if (!shaderA.get() || !shaderB.get()) { +sk_sp SkComposeShader::CreateProc(SkReadBuffer& buffer) { + sk_sp shaderA(buffer.readShader()); + sk_sp shaderB(buffer.readShader()); + sk_sp mode(buffer.readXfermode()); + if (!shaderA || !shaderB) { return nullptr; } - return new SkComposeShader(shaderA, shaderB, mode); + return sk_make_sp(std::move(shaderA), std::move(shaderB), std::move(mode)); } void SkComposeShader::flatten(SkWriteBuffer& buffer) const { - buffer.writeFlattenable(fShaderA); - buffer.writeFlattenable(fShaderB); - buffer.writeFlattenable(fMode); + buffer.writeFlattenable(fShaderA.get()); + buffer.writeFlattenable(fShaderB.get()); + buffer.writeFlattenable(fMode.get()); } template void safe_call_destructor(T* obj) { @@ -77,7 +63,7 @@ template void safe_call_destructor(T* obj) { SkShader::Context* SkComposeShader::onCreateContext(const ContextRec& rec, void* storage) const { char* aStorage = (char*) storage + sizeof(ComposeShaderContext); - char* bStorage = aStorage + fShaderA->contextSize(); + char* bStorage = aStorage + fShaderA->contextSize(rec); // we preconcat our localMatrix (if any) with the device matrix // before calling our sub-shaders @@ -119,9 +105,9 @@ SkComposeShader::ComposeShaderContext::~ComposeShaderContext() { bool SkComposeShader::asACompose(ComposeRec* rec) const { if (rec) { - rec->fShaderA = fShaderA; - rec->fShaderB = fShaderB; - rec->fMode = fMode; + rec->fShaderA = fShaderA.get(); + rec->fShaderB = fShaderB.get(); + rec->fMode = fMode.get(); } return true; } @@ -134,7 +120,7 @@ bool SkComposeShader::asACompose(ComposeRec* rec) const { void SkComposeShader::ComposeShaderContext::shadeSpan(int x, int y, SkPMColor result[], int count) { SkShader::Context* shaderContextA = fShaderContextA; SkShader::Context* shaderContextB = fShaderContextB; - SkXfermode* mode = static_cast(fShader).fMode; + SkXfermode* mode = static_cast(fShader).fMode.get(); unsigned scale = SkAlpha255To256(this->getPaintAlpha()); SkPMColor tmp[TMP_COLOR_COUNT]; @@ -252,3 +238,18 @@ void SkComposeShader::toString(SkString* str) const { str->append(")"); } #endif + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +sk_sp SkShader::MakeComposeShader(sk_sp dst, sk_sp src, + sk_sp xfer) { + if (!dst || !src) { + return nullptr; + } + return sk_make_sp(std::move(dst), std::move(src), std::move(xfer)); +} + +sk_sp SkShader::MakeComposeShader(sk_sp dst, sk_sp src, + SkXfermode::Mode mode) { + return MakeComposeShader(std::move(dst), std::move(src), SkXfermode::Make(mode)); +} diff --git a/gfx/skia/skia/include/core/SkComposeShader.h b/gfx/skia/skia/src/core/SkComposeShader.h similarity index 83% rename from gfx/skia/skia/include/core/SkComposeShader.h rename to gfx/skia/skia/src/core/SkComposeShader.h index bc9d932ee5..4d561faac7 100644 --- a/gfx/skia/skia/include/core/SkComposeShader.h +++ b/gfx/skia/skia/src/core/SkComposeShader.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,13 +5,11 @@ * found in the LICENSE file. */ - #ifndef SkComposeShader_DEFINED #define SkComposeShader_DEFINED #include "SkShader.h" - -class SkXfermode; +#include "SkXfermode.h" /////////////////////////////////////////////////////////////////////////////////////////// @@ -31,10 +28,11 @@ public: @param mode The xfermode that combines the colors from the two shaders. If mode is null, then SRC_OVER is assumed. */ - SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode = NULL); - virtual ~SkComposeShader(); - - size_t contextSize() const override; + SkComposeShader(sk_sp sA, sk_sp sB, sk_sp mode) + : fShaderA(std::move(sA)) + , fShaderB(std::move(sB)) + , fMode(std::move(mode)) + {} #if SK_SUPPORT_GPU const GrFragmentProcessor* asFragmentProcessor(GrContext*, @@ -65,8 +63,8 @@ public: }; #ifdef SK_DEBUG - SkShader* getShaderA() { return fShaderA; } - SkShader* getShaderB() { return fShaderB; } + SkShader* getShaderA() { return fShaderA.get(); } + SkShader* getShaderB() { return fShaderB.get(); } #endif bool asACompose(ComposeRec* rec) const override; @@ -75,14 +73,15 @@ public: SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeShader) protected: - SkComposeShader(SkReadBuffer& ); + SkComposeShader(SkReadBuffer&); void flatten(SkWriteBuffer&) const override; + size_t onContextSize(const ContextRec&) const override; Context* onCreateContext(const ContextRec&, void*) const override; private: - SkShader* fShaderA; - SkShader* fShaderB; - SkXfermode* fMode; + sk_sp fShaderA; + sk_sp fShaderB; + sk_sp fMode; typedef SkShader INHERITED; }; diff --git a/gfx/skia/skia/src/core/SkConfig8888.cpp b/gfx/skia/skia/src/core/SkConfig8888.cpp index 53f35c3737..5bac16a272 100644 --- a/gfx/skia/skia/src/core/SkConfig8888.cpp +++ b/gfx/skia/skia/src/core/SkConfig8888.cpp @@ -129,7 +129,7 @@ bool SkSrcPixelInfo::convertPixelsTo(SkDstPixelInfo* dst, int width, int height) static void copy_g8_to_32(void* dst, size_t dstRB, const void* src, size_t srcRB, int w, int h) { uint32_t* dst32 = (uint32_t*)dst; const uint8_t* src8 = (const uint8_t*)src; - + for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { dst32[x] = SkPackARGB32(0xFF, src8[x], src8[x], src8[x]); @@ -177,6 +177,17 @@ bool SkPixelInfo::CopyPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t const int width = srcInfo.width(); const int height = srcInfo.height(); + // Do the easiest one first : both configs are equal + if ((srcInfo == dstInfo) && !ctable) { + size_t bytes = width * srcInfo.bytesPerPixel(); + for (int y = 0; y < height; ++y) { + memcpy(dstPixels, srcPixels, bytes); + srcPixels = (const char*)srcPixels + srcRB; + dstPixels = (char*)dstPixels + dstRB; + } + return true; + } + // Handle fancy alpha swizzling if both are ARGB32 if (4 == srcInfo.bytesPerPixel() && 4 == dstInfo.bytesPerPixel()) { SkDstPixelInfo dstPI; @@ -292,4 +303,3 @@ bool SkPixelInfo::CopyPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t return true; } } - diff --git a/gfx/skia/skia/src/core/SkCoreBlitters.h b/gfx/skia/skia/src/core/SkCoreBlitters.h index b327039682..ec9ee203ef 100644 --- a/gfx/skia/skia/src/core/SkCoreBlitters.h +++ b/gfx/skia/skia/src/core/SkCoreBlitters.h @@ -49,6 +49,7 @@ protected: uint32_t fShaderFlags; const SkShader* fShader; SkShader::Context* fShaderContext; + bool fConstInY; private: // illegal @@ -177,7 +178,6 @@ private: SkBlitRow::Proc32 fProc32; SkBlitRow::Proc32 fProc32Blend; bool fShadeDirectlyIntoDevice; - bool fConstInY; // illegal SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&); @@ -185,6 +185,14 @@ private: typedef SkShaderBlitter INHERITED; }; +SkBlitter* SkBlitter_ARGB32_Create(const SkPixmap& device, const SkPaint& paint, + SkShader::Context* shaderContext, + SkTBlitterAllocator* allocator); + +SkBlitter* SkBlitter_ARGB64_Create(const SkPixmap& device, const SkPaint& paint, + SkShader::Context* shaderContext, + SkTBlitterAllocator* allocator); + /////////////////////////////////////////////////////////////////////////////// /* These return the correct subclass of blitter for their device config. diff --git a/gfx/skia/skia/src/core/SkCubicClipper.cpp b/gfx/skia/skia/src/core/SkCubicClipper.cpp index 469fc222e3..b5b7dceabe 100644 --- a/gfx/skia/skia/src/core/SkCubicClipper.cpp +++ b/gfx/skia/skia/src/core/SkCubicClipper.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2009 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkCubicClipper.h b/gfx/skia/skia/src/core/SkCubicClipper.h index d7dc381233..617086edb9 100644 --- a/gfx/skia/skia/src/core/SkCubicClipper.h +++ b/gfx/skia/skia/src/core/SkCubicClipper.h @@ -1,4 +1,3 @@ - /* * Copyright 2009 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkData.cpp b/gfx/skia/skia/src/core/SkData.cpp index 5bf833e1f3..995a30a542 100644 --- a/gfx/skia/skia/src/core/SkData.cpp +++ b/gfx/skia/skia/src/core/SkData.cpp @@ -59,9 +59,9 @@ size_t SkData::copyRange(size_t offset, size_t length, void* buffer) const { return length; } -SkData* SkData::PrivateNewWithCopy(const void* srcOrNull, size_t length) { +sk_sp SkData::PrivateNewWithCopy(const void* srcOrNull, size_t length) { if (0 == length) { - return SkData::NewEmpty(); + return SkData::MakeEmpty(); } const size_t actualLength = length + sizeof(SkData); @@ -75,14 +75,15 @@ SkData* SkData::PrivateNewWithCopy(const void* srcOrNull, size_t length) { if (srcOrNull) { memcpy(data->writable_data(), srcOrNull, length); } - return data; + return sk_sp(data); } /////////////////////////////////////////////////////////////////////////////// SK_DECLARE_STATIC_ONCE_PTR(SkData, gEmpty); -SkData* SkData::NewEmpty() { - return SkRef(gEmpty.get([]{return new SkData(nullptr, 0, nullptr, nullptr); })); +sk_sp SkData::MakeEmpty() { + SkData* data = SkRef(gEmpty.get([]{return new SkData(nullptr, 0, nullptr, nullptr); })); + return sk_sp(data); } // assumes fPtr was allocated via sk_malloc @@ -90,21 +91,21 @@ static void sk_free_releaseproc(const void* ptr, void*) { sk_free((void*)ptr); } -SkData* SkData::NewFromMalloc(const void* data, size_t length) { - return new SkData(data, length, sk_free_releaseproc, nullptr); +sk_sp SkData::MakeFromMalloc(const void* data, size_t length) { + return sk_sp(new SkData(data, length, sk_free_releaseproc, nullptr)); } -SkData* SkData::NewWithCopy(const void* src, size_t length) { +sk_sp SkData::MakeWithCopy(const void* src, size_t length) { SkASSERT(src); return PrivateNewWithCopy(src, length); } -SkData* SkData::NewUninitialized(size_t length) { +sk_sp SkData::MakeUninitialized(size_t length) { return PrivateNewWithCopy(nullptr, length); } -SkData* SkData::NewWithProc(const void* ptr, size_t length, ReleaseProc proc, void* context) { - return new SkData(ptr, length, proc, context); +sk_sp SkData::MakeWithProc(const void* ptr, size_t length, ReleaseProc proc, void* ctx) { + return sk_sp(new SkData(ptr, length, proc, ctx)); } // assumes fPtr was allocated with sk_fmmap @@ -113,34 +114,34 @@ static void sk_mmap_releaseproc(const void* addr, void* ctx) { sk_fmunmap(addr, length); } -SkData* SkData::NewFromFILE(FILE* f) { +sk_sp SkData::MakeFromFILE(FILE* f) { size_t size; void* addr = sk_fmmap(f, &size); if (nullptr == addr) { return nullptr; } - return SkData::NewWithProc(addr, size, sk_mmap_releaseproc, reinterpret_cast(size)); + return SkData::MakeWithProc(addr, size, sk_mmap_releaseproc, reinterpret_cast(size)); } -SkData* SkData::NewFromFileName(const char path[]) { +sk_sp SkData::MakeFromFileName(const char path[]) { FILE* f = path ? sk_fopen(path, kRead_SkFILE_Flag) : nullptr; if (nullptr == f) { return nullptr; } - SkData* data = NewFromFILE(f); + auto data = MakeFromFILE(f); sk_fclose(f); return data; } -SkData* SkData::NewFromFD(int fd) { +sk_sp SkData::MakeFromFD(int fd) { size_t size; void* addr = sk_fdmmap(fd, &size); if (nullptr == addr) { return nullptr; } - return SkData::NewWithProc(addr, size, sk_mmap_releaseproc, nullptr); + return SkData::MakeWithProc(addr, size, sk_mmap_releaseproc, nullptr); } // assumes context is a SkData @@ -149,7 +150,7 @@ static void sk_dataref_releaseproc(const void*, void* context) { src->unref(); } -SkData* SkData::NewSubset(const SkData* src, size_t offset, size_t length) { +sk_sp SkData::MakeSubset(const SkData* src, size_t offset, size_t length) { /* We could, if we wanted/need to, just make a deep copy of src's data, rather than referencing it. This would duplicate the storage (of the @@ -158,7 +159,7 @@ SkData* SkData::NewSubset(const SkData* src, size_t offset, size_t length) { size_t available = src->size(); if (offset >= available || 0 == length) { - return SkData::NewEmpty(); + return SkData::MakeEmpty(); } available -= offset; if (length > available) { @@ -167,11 +168,11 @@ SkData* SkData::NewSubset(const SkData* src, size_t offset, size_t length) { SkASSERT(length > 0); src->ref(); // this will be balanced in sk_dataref_releaseproc - return new SkData(src->bytes() + offset, length, sk_dataref_releaseproc, - const_cast(src)); + return sk_sp(new SkData(src->bytes() + offset, length, sk_dataref_releaseproc, + const_cast(src))); } -SkData* SkData::NewWithCString(const char cstr[]) { +sk_sp SkData::MakeWithCString(const char cstr[]) { size_t size; if (nullptr == cstr) { cstr = ""; @@ -179,16 +180,15 @@ SkData* SkData::NewWithCString(const char cstr[]) { } else { size = strlen(cstr) + 1; } - return NewWithCopy(cstr, size); + return MakeWithCopy(cstr, size); } /////////////////////////////////////////////////////////////////////////////// -SkData* SkData::NewFromStream(SkStream* stream, size_t size) { - SkAutoDataUnref data(SkData::NewUninitialized(size)); +sk_sp SkData::MakeFromStream(SkStream* stream, size_t size) { + sk_sp data(SkData::MakeUninitialized(size)); if (stream->read(data->writable_data(), size) != size) { return nullptr; } - return data.detach(); + return data; } - diff --git a/gfx/skia/skia/src/core/SkDebug.cpp b/gfx/skia/skia/src/core/SkDebug.cpp index 0a3a976771..b0cb6c1242 100644 --- a/gfx/skia/skia/src/core/SkDebug.cpp +++ b/gfx/skia/skia/src/core/SkDebug.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -6,58 +5,8 @@ * found in the LICENSE file. */ - #include "SkTypes.h" -#ifdef SK_DEBUG - -int8_t SkToS8(intmax_t x) { - SkASSERT((int8_t)x == x); - return (int8_t)x; -} - -uint8_t SkToU8(uintmax_t x) { - SkASSERT((uint8_t)x == x); - return (uint8_t)x; -} - -int16_t SkToS16(intmax_t x) { - SkASSERT((int16_t)x == x); - return (int16_t)x; -} - -uint16_t SkToU16(uintmax_t x) { - SkASSERT((uint16_t)x == x); - return (uint16_t)x; -} - -int32_t SkToS32(intmax_t x) { - SkASSERT((int32_t)x == x); - return (int32_t)x; -} - -uint32_t SkToU32(uintmax_t x) { - SkASSERT((uint32_t)x == x); - return (uint32_t)x; -} - -int SkToInt(intmax_t x) { - SkASSERT((int)x == x); - return (int)x; -} - -unsigned SkToUInt(uintmax_t x) { - SkASSERT((unsigned)x == x); - return (unsigned)x; -} - -size_t SkToSizeT(uintmax_t x) { - SkASSERT((size_t)x == x); - return (size_t)x; -} - -#endif - #if defined(GOOGLE3) void SkDebugfForDumpStackTrace(const char* data, void* unused) { SkDebugf("%s", data); diff --git a/gfx/skia/skia/src/core/SkDeque.cpp b/gfx/skia/skia/src/core/SkDeque.cpp index 2e469d1faf..f9ab4af531 100644 --- a/gfx/skia/skia/src/core/SkDeque.cpp +++ b/gfx/skia/skia/src/core/SkDeque.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkDescriptor.h b/gfx/skia/skia/src/core/SkDescriptor.h index 1f6f4a8657..0f13acde97 100644 --- a/gfx/skia/skia/src/core/SkDescriptor.h +++ b/gfx/skia/skia/src/core/SkDescriptor.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkDevice.cpp b/gfx/skia/skia/src/core/SkDevice.cpp index 8ff5827ada..1b22856528 100644 --- a/gfx/skia/skia/src/core/SkDevice.cpp +++ b/gfx/skia/skia/src/core/SkDevice.cpp @@ -17,6 +17,7 @@ #include "SkRasterClip.h" #include "SkRSXform.h" #include "SkShader.h" +#include "SkSpecialImage.h" #include "SkTextBlobRunIterator.h" #include "SkTextToPathIter.h" @@ -87,11 +88,11 @@ void SkBaseDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer, void SkBaseDevice::drawPatch(const SkDraw& draw, const SkPoint cubics[12], const SkColor colors[4], const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) { SkPatchUtils::VertexData data; - + SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, draw.fMatrix); // It automatically adjusts lodX and lodY in case it exceeds the number of indices. - // If it fails to generate the vertices, then we do not draw. + // If it fails to generate the vertices, then we do not draw. if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) { this->drawVertices(draw, SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints, data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount, @@ -175,7 +176,7 @@ void SkBaseDevice::drawImageNine(const SkDraw& draw, const SkImage* image, const void SkBaseDevice::drawBitmapNine(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst, const SkPaint& paint) { SkNinePatchIter iter(bitmap.width(), bitmap.height(), center, dst); - + SkRect srcR, dstR; while (iter.next(&srcR, &dstR)) { this->drawBitmapRect(draw, bitmap, &srcR, dstR, paint, SkCanvas::kStrict_SrcRectConstraint); @@ -191,25 +192,24 @@ void SkBaseDevice::drawAtlas(const SkDraw& draw, const SkImage* atlas, const SkR for (int i = 0; i < count; ++i) { SkPoint quad[4]; xform[i].toQuad(tex[i].width(), tex[i].height(), quad); - + SkMatrix localM; localM.setRSXform(xform[i]); localM.preTranslate(-tex[i].left(), -tex[i].top()); SkPaint pnt(paint); - SkAutoTUnref shader(atlas->newShader(SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode, - &localM)); + sk_sp shader = atlas->makeShader(SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, + &localM); if (!shader) { break; } - pnt.setShader(shader); + pnt.setShader(std::move(shader)); if (colors) { - SkAutoTUnref cf(SkColorFilter::CreateModeFilter(colors[i], mode)); - pnt.setColorFilter(cf); + pnt.setColorFilter(SkColorFilter::MakeModeFilter(colors[i], mode)); } - + path.rewind(); path.addPoly(quad, 4, true); path.setConvexity(SkPath::kConvex_Convexity); @@ -283,24 +283,24 @@ bool SkBaseDevice::peekPixels(SkPixmap* pmap) { static void morphpoints(SkPoint dst[], const SkPoint src[], int count, SkPathMeasure& meas, const SkMatrix& matrix) { SkMatrix::MapXYProc proc = matrix.getMapXYProc(); - + for (int i = 0; i < count; i++) { SkPoint pos; SkVector tangent; - + proc(matrix, src[i].fX, src[i].fY, &pos); SkScalar sx = pos.fX; SkScalar sy = pos.fY; - + if (!meas.getPosTan(sx, &pos, &tangent)) { // set to 0 if the measure failed, so that we just set dst == pos tangent.set(0, 0); } - + /* This is the old way (that explains our approach but is way too slow SkMatrix matrix; SkPoint pt; - + pt.set(sx, sy); matrix.setSinCos(tangent.fY, tangent.fX); matrix.preTranslate(-sx, 0); @@ -313,7 +313,7 @@ static void morphpoints(SkPoint dst[], const SkPoint src[], int count, } /* TODO - + Need differentially more subdivisions when the follow-path is curvy. Not sure how to determine that, but we need it. I guess a cheap answer is let the caller tell us, but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out. @@ -323,7 +323,7 @@ static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas, SkPath::Iter iter(src, false); SkPoint srcP[4], dstP[3]; SkPath::Verb verb; - + while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kMove_Verb: @@ -359,16 +359,16 @@ void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t b const SkPath& follow, const SkMatrix* matrix, const SkPaint& paint) { SkASSERT(byteLength == 0 || text != nullptr); - + // nothing to draw if (text == nullptr || byteLength == 0 || draw.fRC->isEmpty()) { return; } - + SkTextToPathIter iter((const char*)text, byteLength, paint, true); SkPathMeasure meas(follow, false); SkScalar hOffset = 0; - + // need to measure first if (paint.getTextAlign() != SkPaint::kLeft_Align) { SkScalar pathLen = meas.getLength(); @@ -377,19 +377,19 @@ void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t b } hOffset += pathLen; } - + const SkPath* iterPath; SkScalar xpos; SkMatrix scaledMatrix; SkScalar scale = iter.getPathScale(); - + scaledMatrix.setScale(scale, scale); - + while (iter.next(&iterPath, &xpos)) { if (iterPath) { SkPath tmp; SkMatrix m(scaledMatrix); - + tmp.setIsVolatile(true); m.postTranslate(xpos + hOffset, 0); if (matrix) { @@ -403,26 +403,36 @@ void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t b ////////////////////////////////////////////////////////////////////////////////////////// -void SkBaseDevice::drawBitmapAsSprite(const SkDraw& draw, const SkBitmap& bitmap, int x, int y, - const SkPaint& paint) { +void SkBaseDevice::drawSpriteWithFilter(const SkDraw& draw, const SkBitmap& bitmap, + int x, int y, + const SkPaint& paint) { SkImageFilter* filter = paint.getImageFilter(); - if (filter && !this->canHandleImageFilter(filter)) { + SkASSERT(filter); + + if (!this->canHandleImageFilter(filter)) { SkImageFilter::DeviceProxy proxy(this); - SkBitmap dst; SkIPoint offset = SkIPoint::Make(0, 0); SkMatrix matrix = *draw.fMatrix; matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); -#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS - const SkIRect clipBounds = bitmap.bounds(); -#else const SkIRect clipBounds = draw.fClip->getBounds().makeOffset(-x, -y); -#endif SkAutoTUnref cache(this->getImageFilterCache()); SkImageFilter::Context ctx(matrix, clipBounds, cache.get()); - if (filter->filterImage(&proxy, bitmap, ctx, &dst, &offset)) { + + sk_sp srcImg(SkSpecialImage::internal_fromBM(&proxy, bitmap, + &this->surfaceProps())); + if (!srcImg) { + return; // something disastrous happened + } + + sk_sp resultImg(filter->filterImage(srcImg.get(), ctx, &offset)); + if (resultImg) { SkPaint tmpUnfiltered(paint); tmpUnfiltered.setImageFilter(nullptr); - this->drawSprite(draw, dst, x + offset.x(), y + offset.y(), tmpUnfiltered); + SkBitmap resultBM; + if (resultImg->internal_getBM(&resultBM)) { + // TODO: add drawSprite(SkSpecialImage) to SkDevice? (see skbug.com/5073) + this->drawSprite(draw, resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered); + } } } else { this->drawSprite(draw, bitmap, x, y, paint); @@ -446,3 +456,6 @@ uint32_t SkBaseDevice::filterTextFlags(const SkPaint& paint) const { return flags; } +sk_sp SkBaseDevice::makeSurface(SkImageInfo const&, SkSurfaceProps const&) { + return nullptr; +} diff --git a/gfx/skia/skia/src/core/SkDeviceProfile.h b/gfx/skia/skia/src/core/SkDeviceProfile.h index 2872b13458..ed533f8329 100644 --- a/gfx/skia/skia/src/core/SkDeviceProfile.h +++ b/gfx/skia/skia/src/core/SkDeviceProfile.h @@ -12,7 +12,7 @@ class SkDeviceProfile : public SkRefCnt { public: - + enum LCDConfig { kNone_LCDConfig, // disables LCD text rendering, uses A8 instead diff --git a/gfx/skia/skia/src/core/SkDistanceFieldGen.cpp b/gfx/skia/skia/src/core/SkDistanceFieldGen.cpp index 147aefad79..187a5bf166 100644 --- a/gfx/skia/skia/src/core/SkDistanceFieldGen.cpp +++ b/gfx/skia/skia/src/core/SkDistanceFieldGen.cpp @@ -316,14 +316,20 @@ static void B2(DFData* curr, int width) { #define DUMP_EDGE 0 #if !DUMP_EDGE -static unsigned char pack_distance_field_val(float dist, float distanceMagnitude) { - if (dist <= -distanceMagnitude) { - return 255; - } else if (dist > distanceMagnitude) { - return 0; - } else { - return (unsigned char)((distanceMagnitude-dist)*128.0f/distanceMagnitude); - } +template +static unsigned char pack_distance_field_val(float dist) { + // The distance field is constructed as unsigned char values, so that the zero value is at 128, + // Beside 128, we have 128 values in range [0, 128), but only 127 values in range (128, 255]. + // So we multiply distanceMagnitude by 127/128 at the latter range to avoid overflow. + dist = SkScalarPin(-dist, -distanceMagnitude, distanceMagnitude * 127.0f / 128.0f); + + // Scale into the positive range for unsigned distance. + dist += distanceMagnitude; + + // Scale into unsigned char range. + // Round to place negative and positive values as equally as possible around 128 + // (which represents zero). + return (unsigned char)SkScalarRoundToInt(dist / (2 * distanceMagnitude) * 256.0f); } #endif @@ -441,7 +447,7 @@ static bool generate_distance_field_from_image(unsigned char* distanceField, } else { dist = SkScalarSqrt(currData->fDistSq); } - *dfPtr++ = pack_distance_field_val(dist, (float)SK_DistanceFieldMagnitude); + *dfPtr++ = pack_distance_field_val(dist); #endif ++currData; ++currEdge; diff --git a/gfx/skia/skia/src/core/SkDistanceFieldGen.h b/gfx/skia/skia/src/core/SkDistanceFieldGen.h index 4a09ace107..5e7af52a54 100644 --- a/gfx/skia/skia/src/core/SkDistanceFieldGen.h +++ b/gfx/skia/skia/src/core/SkDistanceFieldGen.h @@ -10,7 +10,7 @@ #include "SkTypes.h" // the max magnitude for the distance field -// distance values are limited to the range [-SK_DistanceFieldMagnitude, SK_DistanceFieldMagnitude) +// distance values are limited to the range (-SK_DistanceFieldMagnitude, SK_DistanceFieldMagnitude] #define SK_DistanceFieldMagnitude 4 // we need to pad around the original glyph to allow our maximum distance of // SK_DistanceFieldMagnitude texels away from any edge @@ -18,9 +18,10 @@ // the rect we render with is inset from the distance field glyph size to allow for bilerp #define SK_DistanceFieldInset 2 -// for the fragment shader -// The distance field is constructed as unsigned char values, so that the zero value is at 128, -// and the range is [-4, 4 - 1/255). Hence our multiplier is 8 - 1/32 and zero threshold is 128/255. +// For the fragment shader: +// The distance field is constructed as unsigned char values, +// so that the zero value is at 128, and the supported range of distances is [-4 * 127/128, 4]. +// Hence our multiplier (width of the range) is 4 * 255/128 and zero threshold is 128/255. #define SK_DistanceFieldMultiplier "7.96875" #define SK_DistanceFieldThreshold "0.50196078431" diff --git a/gfx/skia/skia/src/core/SkDither.cpp b/gfx/skia/skia/src/core/SkDither.cpp index 546d4799c8..d966e0e910 100644 --- a/gfx/skia/skia/src/core/SkDither.cpp +++ b/gfx/skia/skia/src/core/SkDither.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * diff --git a/gfx/skia/skia/src/core/SkDither.h b/gfx/skia/skia/src/core/SkDither.h index d82b4167b5..463b50403d 100644 --- a/gfx/skia/skia/src/core/SkDither.h +++ b/gfx/skia/skia/src/core/SkDither.h @@ -1,4 +1,3 @@ - /* * Copyright 2008 The Android Open Source Project * diff --git a/gfx/skia/skia/src/doc/SkDocument.cpp b/gfx/skia/skia/src/core/SkDocument.cpp similarity index 100% rename from gfx/skia/skia/src/doc/SkDocument.cpp rename to gfx/skia/skia/src/core/SkDocument.cpp diff --git a/gfx/skia/skia/src/core/SkDraw.cpp b/gfx/skia/skia/src/core/SkDraw.cpp index 246c31ba51..8149d19e1c 100644 --- a/gfx/skia/skia/src/core/SkDraw.cpp +++ b/gfx/skia/skia/src/core/SkDraw.cpp @@ -32,6 +32,7 @@ #include "SkTLazy.h" #include "SkUtils.h" #include "SkVertState.h" +#include "SkXfermode.h" #include "SkBitmapProcShader.h" #include "SkDrawProcs.h" @@ -81,10 +82,10 @@ public: SkAutoBitmapShaderInstall(const SkBitmap& src, const SkPaint& paint, const SkMatrix* localMatrix = nullptr) : fPaint(paint) /* makes a copy of the paint */ { - fPaint.setShader(SkCreateBitmapShader(src, SkShader::kClamp_TileMode, - SkShader::kClamp_TileMode, - localMatrix, &fAllocator)); + fPaint.setShader(SkMakeBitmapShader(src, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, localMatrix, &fAllocator)); // we deliberately left the shader with an owner-count of 2 + fPaint.getShader()->ref(); SkASSERT(2 == fPaint.getShader()->getRefCnt()); } @@ -1005,7 +1006,7 @@ DRAW_PATH: this->drawPath(path, paint, nullptr, true); } -static SkScalar compute_res_scale_for_stroking(const SkMatrix& matrix) { +SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) { if (!matrix.hasPerspective()) { SkScalar sx = SkPoint::Length(matrix[SkMatrix::kMScaleX], matrix[SkMatrix::kMSkewY]); SkScalar sy = SkPoint::Length(matrix[SkMatrix::kMSkewX], matrix[SkMatrix::kMScaleY]); @@ -1085,7 +1086,7 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint, cullRectPtr = &cullRect; } doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr, - compute_res_scale_for_stroking(*fMatrix)); + ComputeResScaleForStroking(*fMatrix)); pathPtr = &tmpPath; } @@ -1146,9 +1147,6 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint, proc SK_INIT_TO_AVOID_WARNING; SkDEBUGFAIL("unknown paint cap type"); } -#ifdef SK_SUPPORT_LEGACY_HAIR_IGNORES_CAPS - proc = SkScan::AntiHairPath; -#endif } else { switch (paint->getStrokeCap()) { case SkPaint::kButt_Cap: @@ -1164,9 +1162,6 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint, proc SK_INIT_TO_AVOID_WARNING; SkDEBUGFAIL("unknown paint cap type"); } -#ifdef SK_SUPPORT_LEGACY_HAIR_IGNORES_CAPS - proc = SkScan::HairPath; -#endif } } proc(*devPathPtr, *fRC, blitter); @@ -1408,6 +1403,11 @@ bool SkDraw::ShouldDrawTextAsPaths(const SkPaint& paint, const SkMatrix& ctm) { return true; } + // Glyphs like Emojis can't be rendered as a path. + if (paint.getTypeface() && paint.getTypeface()->hasColorGlyphs()) { + return false; + } + SkMatrix textM; return SkPaint::TooBigToUseCache(ctm, *paint.setTextMatrix(&textM)); } @@ -1441,7 +1441,7 @@ void SkDraw::drawText_asPaths(const char text[], size_t byteLength, } // disable warning : local variable used without having been initialized -#if defined _WIN32 && _MSC_VER >= 1300 +#if defined _WIN32 #pragma warning ( push ) #pragma warning ( disable : 4701 ) #endif @@ -1461,18 +1461,16 @@ public: void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) { position += rounding; - Sk48Dot16 fx = SkScalarTo48Dot16(position.fX); - Sk48Dot16 fy = SkScalarTo48Dot16(position.fY); // Prevent glyphs from being drawn outside of or straddling the edge of device space. - if ((fx >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) || - (fx >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/) || - (fy >> 16) > INT_MAX - (INT16_MAX + UINT16_MAX) || - (fy >> 16) < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) { + if (position.fX > INT_MAX - (INT16_MAX + UINT16_MAX) || + position.fX < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/) || + position.fY > INT_MAX - (INT16_MAX + UINT16_MAX) || + position.fY < INT_MIN - (INT16_MIN + 0 /*UINT16_MIN*/)) { return; } - int left = Sk48Dot16FloorToInt(fx); - int top = Sk48Dot16FloorToInt(fy); + int left = SkScalarFloorToInt(position.fX); + int top = SkScalarFloorToInt(position.fY); SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0); left += glyph.fLeft; @@ -1562,6 +1560,10 @@ private: //////////////////////////////////////////////////////////////////////////////////////////////////// +SkPaint::FakeGamma SkDraw::fakeGamma() const { + return fDevice->imageInfo().isLinear() ? SkPaint::FakeGamma::On : SkPaint::FakeGamma::Off; +} + void SkDraw::drawText(const char text[], size_t byteLength, SkScalar x, SkScalar y, const SkPaint& paint) const { SkASSERT(byteLength == 0 || text != nullptr); @@ -1580,17 +1582,16 @@ void SkDraw::drawText(const char text[], size_t byteLength, return; } - SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix); - SkGlyphCache* cache = autoCache.getCache(); + SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->fakeGamma(), fMatrix); // The Blitter Choose needs to be live while using the blitter below. SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint); SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get()); - DrawOneGlyph drawOneGlyph(*this, paint, cache, wrapper.getBlitter()); + DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter()); SkFindAndPlaceGlyph::ProcessText( paint.getTextEncoding(), text, byteLength, - {x, y}, *fMatrix, paint.getTextAlign(), cache, drawOneGlyph); + {x, y}, *fMatrix, paint.getTextAlign(), cache.get(), drawOneGlyph); } ////////////////////////////////////////////////////////////////////////////// @@ -1609,9 +1610,8 @@ void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength, paint.setStyle(SkPaint::kFill_Style); paint.setPathEffect(nullptr); - SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc(); - SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), nullptr); - SkGlyphCache* cache = autoCache.getCache(); + SkPaint::GlyphCacheProc glyphCacheProc = paint.getGlyphCacheProc(true); + SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->fakeGamma(), nullptr); const char* stop = text + byteLength; SkTextAlignProc alignProc(paint.getTextAlign()); @@ -1619,10 +1619,10 @@ void SkDraw::drawPosText_asPaths(const char text[], size_t byteLength, // Now restore the original settings, so we "draw" with whatever style/stroking. paint.setStyle(origPaint.getStyle()); - paint.setPathEffect(origPaint.getPathEffect()); + paint.setPathEffect(sk_ref_sp(origPaint.getPathEffect())); while (text < stop) { - const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0); + const SkGlyph& glyph = glyphCacheProc(cache.get(), &text); if (glyph.fWidth) { const SkPath* path = cache->findPath(glyph); if (path) { @@ -1662,21 +1662,20 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, return; } - SkAutoGlyphCache autoCache(paint, &fDevice->surfaceProps(), fMatrix); - SkGlyphCache* cache = autoCache.getCache(); + SkAutoGlyphCache cache(paint, &fDevice->surfaceProps(), this->fakeGamma(), fMatrix); // The Blitter Choose needs to be live while using the blitter below. SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint); SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get()); - DrawOneGlyph drawOneGlyph(*this, paint, cache, wrapper.getBlitter()); + DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter()); SkPaint::Align textAlignment = paint.getTextAlign(); SkFindAndPlaceGlyph::ProcessPosText( paint.getTextEncoding(), text, byteLength, - offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache, drawOneGlyph); + offset, *fMatrix, pos, scalarsPerPosition, textAlignment, cache.get(), drawOneGlyph); } -#if defined _WIN32 && _MSC_VER >= 1300 +#if defined _WIN32 #pragma warning ( pop ) #endif @@ -1701,37 +1700,54 @@ static bool texture_to_matrix(const VertState& state, const SkPoint verts[], class SkTriColorShader : public SkShader { public: - SkTriColorShader() {} - - size_t contextSize() const override; + SkTriColorShader(); class TriColorShaderContext : public SkShader::Context { public: TriColorShaderContext(const SkTriColorShader& shader, const ContextRec&); virtual ~TriColorShaderContext(); - - bool setup(const SkPoint pts[], const SkColor colors[], int, int, int); - void shadeSpan(int x, int y, SkPMColor dstC[], int count) override; private: + bool setup(const SkPoint pts[], const SkColor colors[], int, int, int); + SkMatrix fDstToUnit; SkPMColor fColors[3]; + bool fSetup; typedef SkShader::Context INHERITED; }; + struct TriColorShaderData { + const SkPoint* pts; + const SkColor* colors; + const VertState *state; + }; + SK_TO_STRING_OVERRIDE() // For serialization. This will never be called. Factory getFactory() const override { sk_throw(); return nullptr; } + // Supply setup data to context from drawing setup + void bindSetupData(TriColorShaderData* setupData) { fSetupData = setupData; } + + // Take the setup data from context when needed. + TriColorShaderData* takeSetupData() { + TriColorShaderData *data = fSetupData; + fSetupData = NULL; + return data; + } + protected: + size_t onContextSize(const ContextRec&) const override; Context* onCreateContext(const ContextRec& rec, void* storage) const override { return new (storage) TriColorShaderContext(*this, rec); } private: + TriColorShaderData *fSetupData; + typedef SkShader INHERITED; }; @@ -1759,6 +1775,7 @@ bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const S if (!this->getCTM().invert(&ctmInv)) { return false; } + // TODO replace INV(m) * INV(ctm) with INV(ctm * m) fDstToUnit.setConcat(im, ctmInv); return true; } @@ -1767,27 +1784,36 @@ bool SkTriColorShader::TriColorShaderContext::setup(const SkPoint pts[], const S #include "SkComposeShader.h" static int ScalarTo256(SkScalar v) { - int scale = SkScalarToFixed(v) >> 8; - if (scale < 0) { - scale = 0; - } - if (scale > 255) { - scale = 255; - } - return SkAlpha255To256(scale); + return static_cast(SkScalarPin(v, 0, 1) * 256 + 0.5); } +SkTriColorShader::SkTriColorShader() + : INHERITED(NULL) + , fSetupData(NULL) {} SkTriColorShader::TriColorShaderContext::TriColorShaderContext(const SkTriColorShader& shader, const ContextRec& rec) - : INHERITED(shader, rec) {} + : INHERITED(shader, rec) + , fSetup(false) {} SkTriColorShader::TriColorShaderContext::~TriColorShaderContext() {} -size_t SkTriColorShader::contextSize() const { +size_t SkTriColorShader::onContextSize(const ContextRec&) const { return sizeof(TriColorShaderContext); } + void SkTriColorShader::TriColorShaderContext::shadeSpan(int x, int y, SkPMColor dstC[], int count) { + SkTriColorShader* parent = static_cast(const_cast(&fShader)); + TriColorShaderData* set = parent->takeSetupData(); + if (set) { + fSetup = setup(set->pts, set->colors, set->state->f0, set->state->f1, set->state->f2); + } + + if (!fSetup) { + // Invalid matrices. Not checked before so no need to assert. + return; + } + const int alphaScale = Sk255To256(this->getPaintAlpha()); SkPoint src; @@ -1858,7 +1884,7 @@ void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count, Thus for texture drawing, we need both texture[] and a shader. */ - SkTriColorShader triShader; // must be above declaration of p + auto triShader = sk_make_sp(); SkPaint p(paint); SkShader* shader = p.getShader(); @@ -1872,24 +1898,17 @@ void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count, } // setup the custom shader (if needed) - SkAutoTUnref composeShader; if (colors) { if (nullptr == textures) { // just colors (no texture) - shader = p.setShader(&triShader); + p.setShader(triShader); + shader = p.getShader(); } else { // colors * texture SkASSERT(shader); - bool releaseMode = false; - if (nullptr == xmode) { - xmode = SkXfermode::Create(SkXfermode::kModulate_Mode); - releaseMode = true; - } - composeShader.reset(new SkComposeShader(&triShader, shader, xmode)); - p.setShader(composeShader); - if (releaseMode) { - xmode->unref(); - } + sk_sp xfer = xmode ? sk_ref_sp(xmode) + : SkXfermode::Make(SkXfermode::kModulate_Mode); + p.setShader(SkShader::MakeComposeShader(triShader, sk_ref_sp(shader), std::move(xfer))); } } @@ -1904,46 +1923,28 @@ void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count, VertState::Proc vertProc = state.chooseProc(vmode); if (textures || colors) { + SkTriColorShader::TriColorShaderData verticesSetup = { vertices, colors, &state }; + while (vertProc(&state)) { if (textures) { SkMatrix tempM; if (texture_to_matrix(state, vertices, textures, &tempM)) { - SkShader::ContextRec rec(p, *fMatrix, &tempM); + SkShader::ContextRec rec(p, *fMatrix, &tempM, + SkBlitter::PreferredShaderDest(fDst.info())); if (!blitter->resetShaderContext(rec)) { continue; } } } if (colors) { - // Find the context for triShader. - SkTriColorShader::TriColorShaderContext* triColorShaderContext; - - SkShader::Context* shaderContext = blitter->getShaderContext(); - SkASSERT(shaderContext); - if (p.getShader() == &triShader) { - triColorShaderContext = - static_cast(shaderContext); - } else { - // The shader is a compose shader and triShader is its first shader. - SkASSERT(p.getShader() == composeShader); - SkASSERT(composeShader->getShaderA() == &triShader); - SkComposeShader::ComposeShaderContext* composeShaderContext = - static_cast(shaderContext); - SkShader::Context* shaderContextA = composeShaderContext->getShaderContextA(); - triColorShaderContext = - static_cast(shaderContextA); - } - - if (!triColorShaderContext->setup(vertices, colors, - state.f0, state.f1, state.f2)) { - continue; - } + triShader->bindSetupData(&verticesSetup); } SkPoint tmp[] = { devVerts[state.f0], devVerts[state.f1], devVerts[state.f2] }; SkScan::FillTriangle(tmp, *fRC, blitter.get()); + triShader->bindSetupData(NULL); } } else { // no colors[] and no texture, stroke hairlines with paint's color. diff --git a/gfx/skia/skia/src/core/SkDrawProcs.h b/gfx/skia/skia/src/core/SkDrawProcs.h index a861a0ad86..15c5cf866a 100644 --- a/gfx/skia/skia/src/core/SkDrawProcs.h +++ b/gfx/skia/skia/src/core/SkDrawProcs.h @@ -51,12 +51,12 @@ public: if (SkPaint::kLeft_Align == fAlign) { dst->set(loc.fX, loc.fY); } else if (SkPaint::kCenter_Align == fAlign) { - dst->set(loc.fX - SkFixedToScalar(glyph.fAdvanceX >> 1), - loc.fY - SkFixedToScalar(glyph.fAdvanceY >> 1)); + dst->set(loc.fX - SkFloatToScalar(glyph.fAdvanceX) / 2, + loc.fY - SkFloatToScalar(glyph.fAdvanceY) / 2); } else { SkASSERT(SkPaint::kRight_Align == fAlign); - dst->set(loc.fX - SkFixedToScalar(glyph.fAdvanceX), - loc.fY - SkFixedToScalar(glyph.fAdvanceY)); + dst->set(loc.fX - SkFloatToScalar(glyph.fAdvanceX), + loc.fY - SkFloatToScalar(glyph.fAdvanceY)); } } private: diff --git a/gfx/skia/skia/src/core/SkDrawable.cpp b/gfx/skia/skia/src/core/SkDrawable.cpp index 8fea38be34..bf6d39cfca 100644 --- a/gfx/skia/skia/src/core/SkDrawable.cpp +++ b/gfx/skia/skia/src/core/SkDrawable.cpp @@ -81,5 +81,5 @@ SkPicture* SkDrawable::onNewPictureSnapshot() { if (false) { draw_bbox(canvas, bounds); } - return recorder.endRecording(); + return recorder.finishRecordingAsPicture().release(); } diff --git a/gfx/skia/skia/src/core/SkEdge.cpp b/gfx/skia/skia/src/core/SkEdge.cpp index c64896f2e0..49df95b568 100644 --- a/gfx/skia/skia/src/core/SkEdge.cpp +++ b/gfx/skia/skia/src/core/SkEdge.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -326,8 +325,9 @@ static inline int SkFDot6UpShift(SkFDot6 x, int upShift) { */ static SkFDot6 cubic_delta_from_line(SkFDot6 a, SkFDot6 b, SkFDot6 c, SkFDot6 d) { - SkFDot6 oneThird = ((a << 3) - ((b << 4) - b) + 6*c + d) * 19 >> 9; - SkFDot6 twoThird = (a + 6*b - ((c << 4) - c) + (d << 3)) * 19 >> 9; + // since our parameters may be negative, we don't use << to avoid ASAN warnings + SkFDot6 oneThird = (a*8 - b*15 + 6*c + d) * 19 >> 9; + SkFDot6 twoThird = (a + 6*b - c*15 + d*8) * 19 >> 9; return SkMax32(SkAbs32(oneThird), SkAbs32(twoThird)); } diff --git a/gfx/skia/skia/src/core/SkEdge.h b/gfx/skia/skia/src/core/SkEdge.h index c3adbf85cb..11669b4f7f 100644 --- a/gfx/skia/skia/src/core/SkEdge.h +++ b/gfx/skia/skia/src/core/SkEdge.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkEdgeBuilder.cpp b/gfx/skia/skia/src/core/SkEdgeBuilder.cpp index 92c8330c16..af68e0ff65 100644 --- a/gfx/skia/skia/src/core/SkEdgeBuilder.cpp +++ b/gfx/skia/skia/src/core/SkEdgeBuilder.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -22,11 +21,67 @@ SkEdgeBuilder::SkEdgeBuilder() : fAlloc(16*1024) { fEdgeList = nullptr; } +SkEdgeBuilder::Combine SkEdgeBuilder::CombineVertical(const SkEdge* edge, SkEdge* last) { + if (last->fCurveCount || last->fDX || edge->fX != last->fX) { + return kNo_Combine; + } + if (edge->fWinding == last->fWinding) { + if (edge->fLastY + 1 == last->fFirstY) { + last->fFirstY = edge->fFirstY; + return kPartial_Combine; + } + if (edge->fFirstY == last->fLastY + 1) { + last->fLastY = edge->fLastY; + return kPartial_Combine; + } + return kNo_Combine; + } + if (edge->fFirstY == last->fFirstY) { + if (edge->fLastY == last->fLastY) { + return kTotal_Combine; + } + if (edge->fLastY < last->fLastY) { + last->fFirstY = edge->fLastY + 1; + return kPartial_Combine; + } + last->fFirstY = last->fLastY + 1; + last->fLastY = edge->fLastY; + last->fWinding = edge->fWinding; + return kPartial_Combine; + } + if (edge->fLastY == last->fLastY) { + if (edge->fFirstY > last->fFirstY) { + last->fLastY = edge->fFirstY - 1; + return kPartial_Combine; + } + last->fLastY = last->fFirstY - 1; + last->fFirstY = edge->fFirstY; + last->fWinding = edge->fWinding; + return kPartial_Combine; + } + return kNo_Combine; +} + +static bool vertical_line(const SkEdge* edge) { + return !edge->fDX && !edge->fCurveCount; +} + void SkEdgeBuilder::addLine(const SkPoint pts[]) { SkEdge* edge = typedAllocThrow(fAlloc); if (edge->setLine(pts[0], pts[1], fShiftUp)) { + if (vertical_line(edge) && fList.count()) { + Combine combine = CombineVertical(edge, *(fList.end() - 1)); + if (kNo_Combine != combine) { + if (kTotal_Combine == combine) { + fList.pop(); + } + goto unallocate_edge; + } + } fList.push(edge); } else { +unallocate_edge: + ; // TODO: unallocate edge from storage... } } @@ -79,6 +134,11 @@ static void setShiftedClip(SkRect* dst, const SkIRect& src, int shift) { SkIntToScalar(src.fBottom >> shift)); } +SkEdgeBuilder::Combine SkEdgeBuilder::checkVertical(const SkEdge* edge, SkEdge** edgePtr) { + return !vertical_line(edge) || edgePtr <= fEdgeList ? kNo_Combine : + CombineVertical(edge, edgePtr[-1]); +} + int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, int shiftUp, bool canCullToTheRight) { SkPath::Iter iter(path, true); @@ -119,7 +179,12 @@ int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, int shift SkASSERT(lineCount <= SkLineClipper::kMaxClippedLineSegments); for (int i = 0; i < lineCount; i++) { if (edge->setLine(lines[i], lines[i + 1], shiftUp)) { - *edgePtr++ = edge++; + Combine combine = checkVertical(edge, edgePtr); + if (kNo_Combine == combine) { + *edgePtr++ = edge++; + } else if (kTotal_Combine == combine) { + --edgePtr; + } } } break; @@ -139,7 +204,12 @@ int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip, int shift break; case SkPath::kLine_Verb: if (edge->setLine(pts[0], pts[1], shiftUp)) { - *edgePtr++ = edge++; + Combine combine = checkVertical(edge, edgePtr); + if (kNo_Combine == combine) { + *edgePtr++ = edge++; + } else if (kTotal_Combine == combine) { + --edgePtr; + } } break; default: diff --git a/gfx/skia/skia/src/core/SkEdgeBuilder.h b/gfx/skia/skia/src/core/SkEdgeBuilder.h index 625465b8a6..59f62870e7 100644 --- a/gfx/skia/skia/src/core/SkEdgeBuilder.h +++ b/gfx/skia/skia/src/core/SkEdgeBuilder.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * @@ -27,6 +26,15 @@ public: SkEdge** edgeList() { return fEdgeList; } private: + enum Combine { + kNo_Combine, + kPartial_Combine, + kTotal_Combine + }; + + static Combine CombineVertical(const SkEdge* edge, SkEdge* last); + Combine checkVertical(const SkEdge* edge, SkEdge** edgePtr); + SkChunkAlloc fAlloc; SkTDArray fList; diff --git a/gfx/skia/skia/src/core/SkEdgeClipper.cpp b/gfx/skia/skia/src/core/SkEdgeClipper.cpp index 55f9192aea..85c519d364 100644 --- a/gfx/skia/skia/src/core/SkEdgeClipper.cpp +++ b/gfx/skia/skia/src/core/SkEdgeClipper.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2009 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkEdgeClipper.h b/gfx/skia/skia/src/core/SkEdgeClipper.h index 16887b4a32..e460c1cd87 100644 --- a/gfx/skia/skia/src/core/SkEdgeClipper.h +++ b/gfx/skia/skia/src/core/SkEdgeClipper.h @@ -1,4 +1,3 @@ - /* * Copyright 2009 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkEmptyShader.h b/gfx/skia/skia/src/core/SkEmptyShader.h index 6453e0e440..528ceeabee 100644 --- a/gfx/skia/skia/src/core/SkEmptyShader.h +++ b/gfx/skia/skia/src/core/SkEmptyShader.h @@ -20,16 +20,16 @@ class SK_API SkEmptyShader : public SkShader { public: SkEmptyShader() {} - size_t contextSize() const override { + SK_TO_STRING_OVERRIDE() + SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader) + +protected: + size_t onContextSize(const ContextRec&) const override { // Even though createContext returns nullptr we have to return a value of at least // sizeof(SkShader::Context) to satisfy SkSmallAllocator. return sizeof(SkShader::Context); } - SK_TO_STRING_OVERRIDE() - SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader) - -protected: SkShader::Context* onCreateContext(const ContextRec&, void*) const override { return nullptr; } diff --git a/gfx/skia/skia/src/core/SkError.cpp b/gfx/skia/skia/src/core/SkError.cpp index 9e4385b489..d85b5ff0cc 100644 --- a/gfx/skia/skia/src/core/SkError.cpp +++ b/gfx/skia/skia/src/core/SkError.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2013 Google Inc. * diff --git a/gfx/skia/skia/src/core/SkErrorInternals.h b/gfx/skia/skia/src/core/SkErrorInternals.h index 65ff231821..d573576099 100644 --- a/gfx/skia/skia/src/core/SkErrorInternals.h +++ b/gfx/skia/skia/src/core/SkErrorInternals.h @@ -1,4 +1,3 @@ - /* * Copyright 2013 Google Inc. * diff --git a/gfx/skia/skia/src/core/SkFDot6.h b/gfx/skia/skia/src/core/SkFDot6.h index b536729829..726aa2e46d 100644 --- a/gfx/skia/skia/src/core/SkFDot6.h +++ b/gfx/skia/skia/src/core/SkFDot6.h @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -10,6 +9,7 @@ #ifndef SkFDot6_DEFINED #define SkFDot6_DEFINED +#include "SkFixed.h" #include "SkScalar.h" #include "SkMath.h" @@ -63,6 +63,7 @@ inline SkFixed SkFDot6ToFixed(SkFDot6 x) { #define SkScalarToFDot6(x) (SkFDot6)((x) * 64) #define SkFDot6ToScalar(x) ((SkScalar)(x) * 0.015625f) +#define SkFDot6ToFloat SkFDot6ToScalar inline SkFixed SkFDot6Div(SkFDot6 a, SkFDot6 b) { SkASSERT(b != 0); diff --git a/gfx/skia/skia/src/core/SkFilterProc.cpp b/gfx/skia/skia/src/core/SkFilterProc.cpp index 290384903e..5049727bde 100644 --- a/gfx/skia/skia/src/core/SkFilterProc.cpp +++ b/gfx/skia/skia/src/core/SkFilterProc.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkFilterProc.h b/gfx/skia/skia/src/core/SkFilterProc.h index 3b20d592d2..7348967fd0 100644 --- a/gfx/skia/skia/src/core/SkFilterProc.h +++ b/gfx/skia/skia/src/core/SkFilterProc.h @@ -1,4 +1,3 @@ - /* * Copyright 2008 The Android Open Source Project * diff --git a/gfx/skia/skia/src/core/SkFindAndPlaceGlyph.h b/gfx/skia/skia/src/core/SkFindAndPlaceGlyph.h index c74a9820e4..48cc0fa144 100644 --- a/gfx/skia/skia/src/core/SkFindAndPlaceGlyph.h +++ b/gfx/skia/skia/src/core/SkFindAndPlaceGlyph.h @@ -347,11 +347,11 @@ private: case SkPaint::kLeft_Align: return {0.0f, 0.0f}; case SkPaint::kCenter_Align: - return {SkFixedToScalar(glyph.fAdvanceX >> 1), - SkFixedToScalar(glyph.fAdvanceY >> 1)}; + return {SkFloatToScalar(glyph.fAdvanceX) / 2, + SkFloatToScalar(glyph.fAdvanceY) / 2}; case SkPaint::kRight_Align: - return {SkFixedToScalar(glyph.fAdvanceX), - SkFixedToScalar(glyph.fAdvanceY)}; + return {SkFloatToScalar(glyph.fAdvanceX), + SkFloatToScalar(glyph.fAdvanceY)}; } // Even though the entire enum is covered above, MVSC doesn't think so. Make it happy. SkFAIL("Should never get here."); @@ -367,7 +367,7 @@ private: static SkPoint SubpixelPositionRounding(SkAxisAlignment axisAlignment) { switch (axisAlignment) { case kX_SkAxisAlignment: - return {SkFixedToScalar(SkGlyph::kSubpixelRound), SK_ScalarHalf}; + return {kSubpixelRounding, SK_ScalarHalf}; case kY_SkAxisAlignment: return {SK_ScalarHalf, kSubpixelRounding}; case kNone_SkAxisAlignment: @@ -381,14 +381,16 @@ private: // produce the correct sub-pixel alignment. If a position is aligned with an axis a shortcut // of 0 is used for the sub-pixel position. static SkIPoint SubpixelAlignment(SkAxisAlignment axisAlignment, SkPoint position) { + // Only the fractional part of position.fX and position.fY matter, because the result of + // this function will just be passed to FixedToSub. switch (axisAlignment) { case kX_SkAxisAlignment: - return {SkScalarToFixed(position.fX + kSubpixelRounding), 0}; + return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding), 0}; case kY_SkAxisAlignment: - return {0, SkScalarToFixed(position.fY + kSubpixelRounding)}; + return {0, SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)}; case kNone_SkAxisAlignment: - return {SkScalarToFixed(position.fX + kSubpixelRounding), - SkScalarToFixed(position.fY + kSubpixelRounding)}; + return {SkScalarToFixed(SkScalarFraction(position.fX) + kSubpixelRounding), + SkScalarToFixed(SkScalarFraction(position.fY) + kSubpixelRounding)}; } SkFAIL("Should not get here."); return {0, 0}; @@ -445,8 +447,8 @@ private: if (metricGlyph.fWidth <= 0) { // Exiting early, be sure to update text pointer. *text = tempText; - return finalPosition + SkPoint{SkFixedToScalar(metricGlyph.fAdvanceX), - SkFixedToScalar(metricGlyph.fAdvanceY)}; + return finalPosition + SkPoint{SkFloatToScalar(metricGlyph.fAdvanceX), + SkFloatToScalar(metricGlyph.fAdvanceY)}; } // Adjust the final position by the alignment adjustment. @@ -463,8 +465,8 @@ private: processOneGlyph(renderGlyph, finalPosition, SubpixelPositionRounding(kAxisAlignment)); } - return finalPosition + SkPoint{SkFixedToScalar(renderGlyph.fAdvanceX), - SkFixedToScalar(renderGlyph.fAdvanceY)}; + return finalPosition + SkPoint{SkFloatToScalar(renderGlyph.fAdvanceX), + SkFloatToScalar(renderGlyph.fAdvanceY)}; } private: @@ -495,14 +497,14 @@ private: SkPoint finalPosition = position; const SkGlyph& glyph = fGlyphFinder->lookupGlyph(text); if (kUseKerning) { - finalPosition += {SkFixedToScalar(fAutoKern.adjust(glyph)), 0.0f}; + finalPosition += {fAutoKern.adjust(glyph), 0.0f}; } if (glyph.fWidth > 0) { finalPosition -= TextAlignmentAdjustment(kTextAlignment, glyph); processOneGlyph(glyph, finalPosition, {SK_ScalarHalf, SK_ScalarHalf}); } - return finalPosition + SkPoint{SkFixedToScalar(glyph.fAdvanceX), - SkFixedToScalar(glyph.fAdvanceY)}; + return finalPosition + SkPoint{SkFloatToScalar(glyph.fAdvanceX), + SkFloatToScalar(glyph.fAdvanceY)}; } private: @@ -562,7 +564,7 @@ private: } static SkPoint MeasureText(LookupGlyph& glyphFinder, const char text[], size_t byteLength) { - SkFixed x = 0, y = 0; + SkScalar x = 0, y = 0; const char* stop = text + byteLength; SkAutoKern autokern; @@ -572,11 +574,11 @@ private: // same advance const SkGlyph& glyph = glyphFinder->lookupGlyph(&text); - x += autokern.adjust(glyph) + glyph.fAdvanceX; - y += glyph.fAdvanceY; + x += autokern.adjust(glyph) + SkFloatToScalar(glyph.fAdvanceX); + y += SkFloatToScalar(glyph.fAdvanceY); } SkASSERT(text == stop); - return {SkFixedToScalar(x), SkFixedToScalar(y)}; + return {x, y}; } }; @@ -587,9 +589,8 @@ inline void SkFindAndPlaceGlyph::ProcessPosText( SkPaint::Align textAlignment, SkGlyphCache* cache, ProcessOneGlyph&& processOneGlyph) { - SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix); + SkAxisAlignment axisAlignment = cache->getScalerContext()->computeAxisAlignmentForHText(); uint32_t mtype = matrix.getType(); - LookupGlyph glyphFinder(textEncoding, cache); // Specialized code for handling the most common case for blink. The while loop is totally @@ -710,7 +711,8 @@ inline void SkFindAndPlaceGlyph::ProcessText( GlyphFindAndPlace findAndPosition{ [&](typename GlyphFindAndPlace::Variants* to_init) { if (cache->isSubpixel()) { - SkAxisAlignment axisAlignment = SkComputeAxisAlignmentForHText(matrix); + SkAxisAlignment axisAlignment = + cache->getScalerContext()->computeAxisAlignmentForHText(); InitSubpixel( to_init, axisAlignment, glyphFinder); } else { diff --git a/gfx/skia/skia/src/core/SkFlattenableSerialization.cpp b/gfx/skia/skia/src/core/SkFlattenableSerialization.cpp index 31602079a3..e9ce09ff1f 100644 --- a/gfx/skia/skia/src/core/SkFlattenableSerialization.cpp +++ b/gfx/skia/skia/src/core/SkFlattenableSerialization.cpp @@ -15,9 +15,9 @@ SkData* SkValidatingSerializeFlattenable(SkFlattenable* flattenable) { SkWriteBuffer writer(SkWriteBuffer::kValidation_Flag); writer.writeFlattenable(flattenable); size_t size = writer.bytesWritten(); - SkData* data = SkData::NewUninitialized(size); + auto data = SkData::MakeUninitialized(size); writer.writeToMemory(data->writable_data()); - return data; + return data.release(); } SkFlattenable* SkValidatingDeserializeFlattenable(const void* data, size_t size, diff --git a/gfx/skia/skia/src/core/SkFontDescriptor.cpp b/gfx/skia/skia/src/core/SkFontDescriptor.cpp index 8348d5fdba..69fdc15432 100644 --- a/gfx/skia/skia/src/core/SkFontDescriptor.cpp +++ b/gfx/skia/skia/src/core/SkFontDescriptor.cpp @@ -58,8 +58,8 @@ static void write_uint(SkWStream* stream, size_t n, uint32_t id) { stream->writePackedUInt(n); } -SkFontDescriptor::SkFontDescriptor(SkStream* stream) { - fStyle = (SkTypeface::Style)stream->readPackedUInt(); +bool SkFontDescriptor::Deserialize(SkStream* stream, SkFontDescriptor* result) { + result->fStyle = (SkTypeface::Style)stream->readPackedUInt(); SkAutoSTMalloc<4, SkFixed> axis; size_t axisCount = 0; @@ -67,13 +67,13 @@ SkFontDescriptor::SkFontDescriptor(SkStream* stream) { for (size_t id; (id = stream->readPackedUInt()) != kSentinel;) { switch (id) { case kFontFamilyName: - read_string(stream, &fFamilyName); + read_string(stream, &result->fFamilyName); break; case kFullName: - read_string(stream, &fFullName); + read_string(stream, &result->fFullName); break; case kPostscriptName: - read_string(stream, &fPostscriptName); + read_string(stream, &result->fPostscriptName); break; case kFontAxes: axisCount = read_uint(stream); @@ -90,17 +90,22 @@ SkFontDescriptor::SkFontDescriptor(SkStream* stream) { break; default: SkDEBUGFAIL("Unknown id used by a font descriptor"); - return; + return false; } } size_t length = stream->readPackedUInt(); if (length > 0) { - SkAutoTUnref data(SkData::NewUninitialized(length)); + sk_sp data(SkData::MakeUninitialized(length)); if (stream->read(data->writable_data(), length) == length) { - fFontData.reset(new SkFontData(new SkMemoryStream(data), index, axis, axisCount)); + result->fFontData.reset(new SkFontData(new SkMemoryStream(data), + index, axis, axisCount)); + } else { + SkDEBUGFAIL("Could not read font data"); + return false; } } + return true; } void SkFontDescriptor::serialize(SkWStream* stream) { diff --git a/gfx/skia/skia/src/core/SkFontDescriptor.h b/gfx/skia/skia/src/core/SkFontDescriptor.h index 21fecdc416..9b87365ed5 100644 --- a/gfx/skia/skia/src/core/SkFontDescriptor.h +++ b/gfx/skia/skia/src/core/SkFontDescriptor.h @@ -8,6 +8,7 @@ #ifndef SkFontDescriptor_DEFINED #define SkFontDescriptor_DEFINED +#include "SkFixed.h" #include "SkStream.h" #include "SkString.h" #include "SkTypeface.h" @@ -34,7 +35,7 @@ public: } bool hasStream() const { return fStream.get() != nullptr; } SkStreamAsset* duplicateStream() const { return fStream->duplicate(); } - SkStreamAsset* detachStream() { return fStream.detach(); } + SkStreamAsset* detachStream() { return fStream.release(); } SkStreamAsset* getStream() { return fStream.get(); } int getIndex() const { return fIndex; } int getAxisCount() const { return fAxisCount; } @@ -51,7 +52,7 @@ class SkFontDescriptor : SkNoncopyable { public: SkFontDescriptor(SkTypeface::Style = SkTypeface::kNormal); // Does not affect ownership of SkStream. - SkFontDescriptor(SkStream*); + static bool Deserialize(SkStream*, SkFontDescriptor* result); void serialize(SkWStream*); @@ -62,7 +63,7 @@ public: const char* getFullName() const { return fFullName.c_str(); } const char* getPostscriptName() const { return fPostscriptName.c_str(); } bool hasFontData() const { return fFontData.get() != nullptr; } - SkFontData* detachFontData() { return fFontData.detach(); } + SkFontData* detachFontData() { return fFontData.release(); } void setFamilyName(const char* name) { fFamilyName.set(name); } void setFullName(const char* name) { fFullName.set(name); } diff --git a/gfx/skia/skia/src/core/SkFontMgr.cpp b/gfx/skia/skia/src/core/SkFontMgr.cpp index 5246916463..ab04250c03 100644 --- a/gfx/skia/skia/src/core/SkFontMgr.cpp +++ b/gfx/skia/skia/src/core/SkFontMgr.cpp @@ -132,6 +132,13 @@ SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, int ttcIndex) con return this->onCreateFromStream(stream, ttcIndex); } +SkTypeface* SkFontMgr::createFromStream(SkStreamAsset* stream, const FontParameters& params) const { + if (nullptr == stream) { + return nullptr; + } + return this->onCreateFromStream(stream, params); +} + SkTypeface* SkFontMgr::createFromFontData(SkFontData* data) const { if (nullptr == data) { return nullptr; @@ -139,6 +146,11 @@ SkTypeface* SkFontMgr::createFromFontData(SkFontData* data) const { return this->onCreateFromFontData(data); } +// This implementation is temporary until it can be made pure virtual. +SkTypeface* SkFontMgr::onCreateFromStream(SkStreamAsset* stream, const FontParameters& p) const { + return this->createFromStream(stream, p.getCollectionIndex()); +} + // This implementation is temporary until it can be made pure virtual. SkTypeface* SkFontMgr::onCreateFromFontData(SkFontData* data) const { SkTypeface* ret = this->createFromStream(data->detachStream(), data->getIndex()); diff --git a/gfx/skia/skia/src/core/SkGeometry.cpp b/gfx/skia/skia/src/core/SkGeometry.cpp index c25e18641a..7256b9e517 100644 --- a/gfx/skia/skia/src/core/SkGeometry.cpp +++ b/gfx/skia/skia/src/core/SkGeometry.cpp @@ -9,21 +9,9 @@ #include "SkMatrix.h" #include "SkNx.h" -#if 0 -static Sk2s from_point(const SkPoint& point) { - return Sk2s::Load(&point.fX); -} - -static SkPoint to_point(const Sk2s& x) { - SkPoint point; - x.store(&point.fX); - return point; -} -#endif - static SkVector to_vector(const Sk2s& x) { SkVector vector; - x.store(&vector.fX); + x.store(&vector); return vector; } @@ -116,44 +104,12 @@ int SkFindUnitQuadRoots(SkScalar A, SkScalar B, SkScalar C, SkScalar roots[2]) { /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// -static Sk2s quad_poly_eval(const Sk2s& A, const Sk2s& B, const Sk2s& C, const Sk2s& t) { - return (A * t + B) * t + C; -} - -static SkScalar eval_quad(const SkScalar src[], SkScalar t) { - SkASSERT(src); - SkASSERT(t >= 0 && t <= SK_Scalar1); - -#ifdef DIRECT_EVAL_OF_POLYNOMIALS - SkScalar C = src[0]; - SkScalar A = src[4] - 2 * src[2] + C; - SkScalar B = 2 * (src[2] - C); - return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); -#else - SkScalar ab = SkScalarInterp(src[0], src[2], t); - SkScalar bc = SkScalarInterp(src[2], src[4], t); - return SkScalarInterp(ab, bc, t); -#endif -} - -void SkQuadToCoeff(const SkPoint pts[3], SkPoint coeff[3]) { - Sk2s p0 = from_point(pts[0]); - Sk2s p1 = from_point(pts[1]); - Sk2s p2 = from_point(pts[2]); - - Sk2s p1minus2 = p1 - p0; - - coeff[0] = to_point(p2 - p1 - p1 + p0); // A * t^2 - coeff[1] = to_point(p1minus2 + p1minus2); // B * t - coeff[2] = pts[0]; // C -} - void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent) { SkASSERT(src); SkASSERT(t >= 0 && t <= SK_Scalar1); if (pt) { - pt->set(eval_quad(&src[0].fX, t), eval_quad(&src[0].fY, t)); + *pt = SkEvalQuadAt(src, t); } if (tangent) { *tangent = SkEvalQuadTangentAt(src, t); @@ -161,19 +117,7 @@ void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tange } SkPoint SkEvalQuadAt(const SkPoint src[3], SkScalar t) { - SkASSERT(src); - SkASSERT(t >= 0 && t <= SK_Scalar1); - - const Sk2s t2(t); - - Sk2s P0 = from_point(src[0]); - Sk2s P1 = from_point(src[1]); - Sk2s P2 = from_point(src[2]); - - Sk2s B = P1 - P0; - Sk2s A = P2 - P1 - B; - - return to_point((A * t2 + B+B) * t2 + P0); + return to_point(SkQuadCoeff(src).eval(t)); } SkVector SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t) { @@ -220,7 +164,7 @@ void SkChopQuadAt(const SkPoint src[3], SkPoint dst[5], SkScalar t) { } void SkChopQuadAtHalf(const SkPoint src[3], SkPoint dst[5]) { - SkChopQuadAt(src, dst, 0.5f); return; + SkChopQuadAt(src, dst, 0.5f); } /** Quad'(t) = At + B, where @@ -345,6 +289,7 @@ void SkConvertQuadToCubic(const SkPoint src[3], SkPoint dst[4]) { ///// CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS // CUBICS ///// ////////////////////////////////////////////////////////////////////////////// +#ifdef SK_SUPPORT_LEGACY_EVAL_CUBIC static SkScalar eval_cubic(const SkScalar src[], SkScalar t) { SkASSERT(src); SkASSERT(t >= 0 && t <= SK_Scalar1); @@ -369,28 +314,30 @@ static SkScalar eval_cubic(const SkScalar src[], SkScalar t) { return SkScalarInterp(abc, bcd, t); #endif } +#endif -/** return At^2 + Bt + C -*/ -static SkScalar eval_quadratic(SkScalar A, SkScalar B, SkScalar C, SkScalar t) { - SkASSERT(t >= 0 && t <= SK_Scalar1); +static SkVector eval_cubic_derivative(const SkPoint src[4], SkScalar t) { + SkQuadCoeff coeff; + Sk2s P0 = from_point(src[0]); + Sk2s P1 = from_point(src[1]); + Sk2s P2 = from_point(src[2]); + Sk2s P3 = from_point(src[3]); - return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); + coeff.fA = P3 + Sk2s(3) * (P1 - P2) - P0; + coeff.fB = times_2(P2 - times_2(P1) + P0); + coeff.fC = P1 - P0; + return to_vector(coeff.eval(t)); } -static SkScalar eval_cubic_derivative(const SkScalar src[], SkScalar t) { - SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0]; - SkScalar B = 2*(src[4] - 2 * src[2] + src[0]); - SkScalar C = src[2] - src[0]; +static SkVector eval_cubic_2ndDerivative(const SkPoint src[4], SkScalar t) { + Sk2s P0 = from_point(src[0]); + Sk2s P1 = from_point(src[1]); + Sk2s P2 = from_point(src[2]); + Sk2s P3 = from_point(src[3]); + Sk2s A = P3 + Sk2s(3) * (P1 - P2) - P0; + Sk2s B = P2 - times_2(P1) + P0; - return eval_quadratic(A, B, C, t); -} - -static SkScalar eval_cubic_2ndDerivative(const SkScalar src[], SkScalar t) { - SkScalar A = src[6] + 3*(src[2] - src[4]) - src[0]; - SkScalar B = src[4] - 2 * src[2] + src[0]; - - return SkScalarMulAdd(A, t, B); + return to_vector(A * Sk2s(t) + B); } void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, @@ -399,7 +346,11 @@ void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, SkASSERT(t >= 0 && t <= SK_Scalar1); if (loc) { +#ifdef SK_SUPPORT_LEGACY_EVAL_CUBIC loc->set(eval_cubic(&src[0].fX, t), eval_cubic(&src[0].fY, t)); +#else + *loc = to_point(SkCubicCoeff(src).eval(t)); +#endif } if (tangent) { // The derivative equation returns a zero tangent vector when t is 0 or 1, and the @@ -415,13 +366,11 @@ void SkEvalCubicAt(const SkPoint src[4], SkScalar t, SkPoint* loc, *tangent = src[3] - src[0]; } } else { - tangent->set(eval_cubic_derivative(&src[0].fX, t), - eval_cubic_derivative(&src[0].fY, t)); + *tangent = eval_cubic_derivative(src, t); } } if (curvature) { - curvature->set(eval_cubic_2ndDerivative(&src[0].fX, t), - eval_cubic_2ndDerivative(&src[0].fY, t)); + *curvature = eval_cubic_2ndDerivative(src, t); } } @@ -466,26 +415,6 @@ void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t) { dst[6] = src[3]; } -void SkCubicToCoeff(const SkPoint pts[4], SkPoint coeff[4]) { - Sk2s p0 = from_point(pts[0]); - Sk2s p1 = from_point(pts[1]); - Sk2s p2 = from_point(pts[2]); - Sk2s p3 = from_point(pts[3]); - - const Sk2s three(3); - Sk2s p1minusp2 = p1 - p2; - - Sk2s D = p0; - Sk2s A = p3 + three * p1minusp2 - D; - Sk2s B = three * (D - p1minusp2 - p1); - Sk2s C = three * (p1 - D); - - coeff[0] = to_point(A); - coeff[1] = to_point(B); - coeff[2] = to_point(C); - coeff[3] = to_point(D); -} - /* http://code.google.com/p/skia/issues/detail?id=32 This test code would fail when we didn't check the return result of @@ -961,179 +890,6 @@ bool SkChopMonoCubicAtX(SkPoint src[4], SkScalar x, SkPoint dst[7]) { return cubic_dchop_at_intercept(src, x, dst, &SkDCubic::verticalIntersect); } -/////////////////////////////////////////////////////////////////////////////// - -/* Find t value for quadratic [a, b, c] = d. - Return 0 if there is no solution within [0, 1) -*/ -static SkScalar quad_solve(SkScalar a, SkScalar b, SkScalar c, SkScalar d) { - // At^2 + Bt + C = d - SkScalar A = a - 2 * b + c; - SkScalar B = 2 * (b - a); - SkScalar C = a - d; - - SkScalar roots[2]; - int count = SkFindUnitQuadRoots(A, B, C, roots); - - SkASSERT(count <= 1); - return count == 1 ? roots[0] : 0; -} - -/* given a quad-curve and a point (x,y), chop the quad at that point and place - the new off-curve point and endpoint into 'dest'. - Should only return false if the computed pos is the start of the curve - (i.e. root == 0) -*/ -static bool truncate_last_curve(const SkPoint quad[3], SkScalar x, SkScalar y, - SkPoint* dest) { - const SkScalar* base; - SkScalar value; - - if (SkScalarAbs(x) < SkScalarAbs(y)) { - base = &quad[0].fX; - value = x; - } else { - base = &quad[0].fY; - value = y; - } - - // note: this returns 0 if it thinks value is out of range, meaning the - // root might return something outside of [0, 1) - SkScalar t = quad_solve(base[0], base[2], base[4], value); - - if (t > 0) { - SkPoint tmp[5]; - SkChopQuadAt(quad, tmp, t); - dest[0] = tmp[1]; - dest[1].set(x, y); - return true; - } else { - /* t == 0 means either the value triggered a root outside of [0, 1) - For our purposes, we can ignore the <= 0 roots, but we want to - catch the >= 1 roots (which given our caller, will basically mean - a root of 1, give-or-take numerical instability). If we are in the - >= 1 case, return the existing offCurve point. - - The test below checks to see if we are close to the "end" of the - curve (near base[4]). Rather than specifying a tolerance, I just - check to see if value is on to the right/left of the middle point - (depending on the direction/sign of the end points). - */ - if ((base[0] < base[4] && value > base[2]) || - (base[0] > base[4] && value < base[2])) // should root have been 1 - { - dest[0] = quad[1]; - dest[1].set(x, y); - return true; - } - } - return false; -} - -static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = { -// The mid point of the quadratic arc approximation is half way between the two -// control points. The float epsilon adjustment moves the on curve point out by -// two bits, distributing the convex test error between the round rect -// approximation and the convex cross product sign equality test. -#define SK_MID_RRECT_OFFSET \ - (SK_Scalar1 + SK_ScalarTanPIOver8 + FLT_EPSILON * 4) / 2 - { SK_Scalar1, 0 }, - { SK_Scalar1, SK_ScalarTanPIOver8 }, - { SK_MID_RRECT_OFFSET, SK_MID_RRECT_OFFSET }, - { SK_ScalarTanPIOver8, SK_Scalar1 }, - - { 0, SK_Scalar1 }, - { -SK_ScalarTanPIOver8, SK_Scalar1 }, - { -SK_MID_RRECT_OFFSET, SK_MID_RRECT_OFFSET }, - { -SK_Scalar1, SK_ScalarTanPIOver8 }, - - { -SK_Scalar1, 0 }, - { -SK_Scalar1, -SK_ScalarTanPIOver8 }, - { -SK_MID_RRECT_OFFSET, -SK_MID_RRECT_OFFSET }, - { -SK_ScalarTanPIOver8, -SK_Scalar1 }, - - { 0, -SK_Scalar1 }, - { SK_ScalarTanPIOver8, -SK_Scalar1 }, - { SK_MID_RRECT_OFFSET, -SK_MID_RRECT_OFFSET }, - { SK_Scalar1, -SK_ScalarTanPIOver8 }, - - { SK_Scalar1, 0 } -#undef SK_MID_RRECT_OFFSET -}; - -int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop, - SkRotationDirection dir, const SkMatrix* userMatrix, - SkPoint quadPoints[]) { - // rotate by x,y so that uStart is (1.0) - SkScalar x = SkPoint::DotProduct(uStart, uStop); - SkScalar y = SkPoint::CrossProduct(uStart, uStop); - - SkScalar absX = SkScalarAbs(x); - SkScalar absY = SkScalarAbs(y); - - int pointCount; - - // check for (effectively) coincident vectors - // this can happen if our angle is nearly 0 or nearly 180 (y == 0) - // ... we use the dot-prod to distinguish between 0 and 180 (x > 0) - if (absY <= SK_ScalarNearlyZero && x > 0 && - ((y >= 0 && kCW_SkRotationDirection == dir) || - (y <= 0 && kCCW_SkRotationDirection == dir))) { - - // just return the start-point - quadPoints[0].set(SK_Scalar1, 0); - pointCount = 1; - } else { - if (dir == kCCW_SkRotationDirection) { - y = -y; - } - // what octant (quadratic curve) is [xy] in? - int oct = 0; - bool sameSign = true; - - if (0 == y) { - oct = 4; // 180 - SkASSERT(SkScalarAbs(x + SK_Scalar1) <= SK_ScalarNearlyZero); - } else if (0 == x) { - SkASSERT(absY - SK_Scalar1 <= SK_ScalarNearlyZero); - oct = y > 0 ? 2 : 6; // 90 : 270 - } else { - if (y < 0) { - oct += 4; - } - if ((x < 0) != (y < 0)) { - oct += 2; - sameSign = false; - } - if ((absX < absY) == sameSign) { - oct += 1; - } - } - - int wholeCount = oct << 1; - memcpy(quadPoints, gQuadCirclePts, (wholeCount + 1) * sizeof(SkPoint)); - - const SkPoint* arc = &gQuadCirclePts[wholeCount]; - if (truncate_last_curve(arc, x, y, &quadPoints[wholeCount + 1])) { - wholeCount += 2; - } - pointCount = wholeCount + 1; - } - - // now handle counter-clockwise and the initial unitStart rotation - SkMatrix matrix; - matrix.setSinCos(uStart.fY, uStart.fX); - if (dir == kCCW_SkRotationDirection) { - matrix.preScale(SK_Scalar1, -SK_Scalar1); - } - if (userMatrix) { - matrix.postConcat(*userMatrix); - } - matrix.mapPoints(quadPoints, pointCount); - return pointCount; -} - - /////////////////////////////////////////////////////////////////////////////// // // NURB representation for conics. Helpful explanations at: @@ -1246,29 +1002,38 @@ void SkConic::chopAt(SkScalar t, SkConic dst[2]) const { dst[1].fW = tmp2[2].fZ / root; } -static Sk2s times_2(const Sk2s& value) { - return value + value; +void SkConic::chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const { + if (0 == t1 || 1 == t2) { + if (0 == t1 && 1 == t2) { + *dst = *this; + } else { + SkConic pair[2]; + this->chopAt(t1 ? t1 : t2, pair); + *dst = pair[SkToBool(t1)]; + } + return; + } + SkConicCoeff coeff(*this); + Sk2s tt1(t1); + Sk2s aXY = coeff.fNumer.eval(tt1); + Sk2s aZZ = coeff.fDenom.eval(tt1); + Sk2s midTT((t1 + t2) / 2); + Sk2s dXY = coeff.fNumer.eval(midTT); + Sk2s dZZ = coeff.fDenom.eval(midTT); + Sk2s tt2(t2); + Sk2s cXY = coeff.fNumer.eval(tt2); + Sk2s cZZ = coeff.fDenom.eval(tt2); + Sk2s bXY = times_2(dXY) - (aXY + cXY) * Sk2s(0.5f); + Sk2s bZZ = times_2(dZZ) - (aZZ + cZZ) * Sk2s(0.5f); + dst->fPts[0] = to_point(aXY / aZZ); + dst->fPts[1] = to_point(bXY / bZZ); + dst->fPts[2] = to_point(cXY / cZZ); + Sk2s ww = bZZ / (aZZ * cZZ).sqrt(); + dst->fW = ww[0]; } SkPoint SkConic::evalAt(SkScalar t) const { - Sk2s p0 = from_point(fPts[0]); - Sk2s p1 = from_point(fPts[1]); - Sk2s p2 = from_point(fPts[2]); - Sk2s tt(t); - Sk2s ww(fW); - Sk2s one(1); - - Sk2s p1w = p1 * ww; - Sk2s C = p0; - Sk2s A = p2 - times_2(p1w) + p0; - Sk2s B = times_2(p1w - C); - Sk2s numer = quad_poly_eval(A, B, C, tt); - - B = times_2(ww - one); - A = Sk2s(0)-B; - Sk2s denom = quad_poly_eval(A, B, one, tt); - - return to_point(numer / denom); + return to_point(SkConicCoeff(*this).eval(t)); } SkVector SkConic::evalTangentAt(SkScalar t) const { @@ -1290,12 +1055,12 @@ SkVector SkConic::evalTangentAt(SkScalar t) const { Sk2s A = ww * p20 - p20; Sk2s B = p20 - C - C; - return to_vector(quad_poly_eval(A, B, C, Sk2s(t))); + return to_vector(SkQuadCoeff(A, B, C).eval(t)); } void SkConic::evalAt(SkScalar t, SkPoint* pt, SkVector* tangent) const { SkASSERT(t >= 0 && t <= SK_Scalar1); - + if (pt) { *pt = this->evalAt(t); } @@ -1308,10 +1073,6 @@ static SkScalar subdivide_w_value(SkScalar w) { return SkScalarSqrt(SK_ScalarHalf + w * SK_ScalarHalf); } -static Sk2s twice(const Sk2s& value) { - return value + value; -} - void SkConic::chop(SkConic * SK_RESTRICT dst) const { Sk2s scale = Sk2s(SkScalarInvert(SK_Scalar1 + fW)); SkScalar newW = subdivide_w_value(fW); @@ -1322,7 +1083,7 @@ void SkConic::chop(SkConic * SK_RESTRICT dst) const { Sk2s ww(fW); Sk2s wp1 = ww * p1; - Sk2s m = (p0 + twice(wp1) + p2) * scale * Sk2s(0.5f); + Sk2s m = (p0 + times_2(wp1) + p2) * scale * Sk2s(0.5f); dst[0].fPts[0] = fPts[0]; dst[0].fPts[1] = to_point((p0 + wp1) * scale); diff --git a/gfx/skia/skia/src/core/SkGeometry.h b/gfx/skia/skia/src/core/SkGeometry.h index 79cad2eee0..15f1e55195 100644 --- a/gfx/skia/skia/src/core/SkGeometry.h +++ b/gfx/skia/skia/src/core/SkGeometry.h @@ -12,18 +12,17 @@ #include "SkNx.h" static inline Sk2s from_point(const SkPoint& point) { - return Sk2s::Load(&point.fX); + return Sk2s::Load(&point); } static inline SkPoint to_point(const Sk2s& x) { SkPoint point; - x.store(&point.fX); + x.store(&point); return point; } -static inline Sk2s sk2s_cubic_eval(const Sk2s& A, const Sk2s& B, const Sk2s& C, const Sk2s& D, - const Sk2s& t) { - return ((A * t + B) * t + C) * t + D; +static Sk2s times_2(const Sk2s& value) { + return value + value; } /** Given a quadratic equation Ax^2 + Bx + C = 0, return 0, 1, 2 roots for the @@ -41,16 +40,6 @@ SkPoint SkEvalQuadTangentAt(const SkPoint src[3], SkScalar t); */ void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint* pt, SkVector* tangent = nullptr); -/** - * output is : eval(t) == coeff[0] * t^2 + coeff[1] * t + coeff[2] - */ -void SkQuadToCoeff(const SkPoint pts[3], SkPoint coeff[3]); - -/** - * output is : eval(t) == coeff[0] * t^3 + coeff[1] * t^2 + coeff[2] * t + coeff[3] - */ -void SkCubicToCoeff(const SkPoint pts[4], SkPoint coeff[4]); - /** Given a src quadratic bezier, chop it at the specified t value, where 0 < t < 1, and return the two new quadratics in dst: dst[0..2] and dst[2..4] @@ -190,20 +179,6 @@ enum SkRotationDirection { kCCW_SkRotationDirection }; -/** Maximum number of points needed in the quadPoints[] parameter for - SkBuildQuadArc() -*/ -#define kSkBuildQuadArcStorage 17 - -/** Given 2 unit vectors and a rotation direction, fill out the specified - array of points with quadratic segments. Return is the number of points - written to, which will be { 0, 3, 5, 7, ... kSkBuildQuadArcStorage } - - matrix, if not null, is appled to the points before they are returned. -*/ -int SkBuildQuadArc(const SkVector& unitStart, const SkVector& unitStop, - SkRotationDirection, const SkMatrix*, SkPoint quadPoints[]); - struct SkConic { SkConic() {} SkConic(const SkPoint& p0, const SkPoint& p1, const SkPoint& p2, SkScalar w) { @@ -241,6 +216,7 @@ struct SkConic { */ void evalAt(SkScalar t, SkPoint* pos, SkVector* tangent = nullptr) const; void chopAt(SkScalar t, SkConic dst[2]) const; + void chopAt(SkScalar t1, SkScalar t2, SkConic* dst) const; void chop(SkConic dst[2]) const; SkPoint evalAt(SkScalar t) const; @@ -287,6 +263,102 @@ struct SkConic { const SkMatrix*, SkConic conics[kMaxConicsForArc]); }; +// inline helpers are contained in a namespace to avoid external leakage to fragile SkNx members +namespace { + +/** + * use for : eval(t) == A * t^2 + B * t + C + */ +struct SkQuadCoeff { + SkQuadCoeff() {} + + SkQuadCoeff(const Sk2s& A, const Sk2s& B, const Sk2s& C) + : fA(A) + , fB(B) + , fC(C) + { + } + + SkQuadCoeff(const SkPoint src[3]) { + fC = from_point(src[0]); + Sk2s P1 = from_point(src[1]); + Sk2s P2 = from_point(src[2]); + fB = times_2(P1 - fC); + fA = P2 - times_2(P1) + fC; + } + + Sk2s eval(SkScalar t) { + Sk2s tt(t); + return eval(tt); + } + + Sk2s eval(const Sk2s& tt) { + return (fA * tt + fB) * tt + fC; + } + + Sk2s fA; + Sk2s fB; + Sk2s fC; +}; + +struct SkConicCoeff { + SkConicCoeff(const SkConic& conic) { + Sk2s p0 = from_point(conic.fPts[0]); + Sk2s p1 = from_point(conic.fPts[1]); + Sk2s p2 = from_point(conic.fPts[2]); + Sk2s ww(conic.fW); + + Sk2s p1w = p1 * ww; + fNumer.fC = p0; + fNumer.fA = p2 - times_2(p1w) + p0; + fNumer.fB = times_2(p1w - p0); + + fDenom.fC = Sk2s(1); + fDenom.fB = times_2(ww - fDenom.fC); + fDenom.fA = Sk2s(0) - fDenom.fB; + } + + Sk2s eval(SkScalar t) { + Sk2s tt(t); + Sk2s numer = fNumer.eval(tt); + Sk2s denom = fDenom.eval(tt); + return numer / denom; + } + + SkQuadCoeff fNumer; + SkQuadCoeff fDenom; +}; + +struct SkCubicCoeff { + SkCubicCoeff(const SkPoint src[4]) { + Sk2s P0 = from_point(src[0]); + Sk2s P1 = from_point(src[1]); + Sk2s P2 = from_point(src[2]); + Sk2s P3 = from_point(src[3]); + Sk2s three(3); + fA = P3 + three * (P1 - P2) - P0; + fB = three * (P2 - times_2(P1) + P0); + fC = three * (P1 - P0); + fD = P0; + } + + Sk2s eval(SkScalar t) { + Sk2s tt(t); + return eval(tt); + } + + Sk2s eval(const Sk2s& t) { + return ((fA * t + fB) * t + fC) * t + fD; + } + + Sk2s fA; + Sk2s fB; + Sk2s fC; + Sk2s fD; +}; + +} + #include "SkTemplates.h" /** diff --git a/gfx/skia/skia/src/core/SkGlyph.h b/gfx/skia/skia/src/core/SkGlyph.h index 92f974bc2c..04f9296b55 100644 --- a/gfx/skia/skia/src/core/SkGlyph.h +++ b/gfx/skia/skia/src/core/SkGlyph.h @@ -34,13 +34,29 @@ class SkGlyph { kSubShiftY = 0 }; - public: + // Support horizontal and vertical skipping strike-through / underlines. + // The caller walks the linked list looking for a match. For a horizontal underline, + // the fBounds contains the top and bottom of the underline. The fInterval pair contains the + // beginning and end of of the intersection of the bounds and the glyph's path. + // If interval[0] >= interval[1], no intesection was found. + struct Intercept { + Intercept* fNext; + SkScalar fBounds[2]; // for horz underlines, the boundaries in Y + SkScalar fInterval[2]; // the outside intersections of the axis and the glyph + }; + + struct PathData { + Intercept* fIntercept; + SkPath* fPath; + }; + +public: static const SkFixed kSubpixelRound = SK_FixedHalf >> SkGlyph::kSubBits; // A value that can never be generated by MakeID. static const uint32_t kImpossibleID = ~0; void* fImage; - SkPath* fPath; - SkFixed fAdvanceX, fAdvanceY; + PathData* fPathData; + float fAdvanceX, fAdvanceY; uint16_t fWidth, fHeight; int16_t fTop, fLeft; @@ -133,7 +149,7 @@ class SkGlyph { void initCommon(uint32_t id) { fID = id; fImage = nullptr; - fPath = nullptr; + fPathData = nullptr; fMaskFormat = MASK_FORMAT_UNKNOWN; fForceBW = 0; } @@ -176,7 +192,7 @@ class SkGlyph { return ID; } - // FIXME - This is needed because the Android frame work directly + // FIXME - This is needed because the Android frame work directly // accesses fID. Remove when fID accesses are cleaned up. #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK public: diff --git a/gfx/skia/skia/src/core/SkGlyphCache.cpp b/gfx/skia/skia/src/core/SkGlyphCache.cpp index 1512dbf0e5..241854eb5a 100644 --- a/gfx/skia/skia/src/core/SkGlyphCache.cpp +++ b/gfx/skia/skia/src/core/SkGlyphCache.cpp @@ -53,7 +53,10 @@ SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkSca } SkGlyphCache::~SkGlyphCache() { - fGlyphMap.foreach ([](SkGlyph* g) { delete g->fPath; }); + fGlyphMap.foreach ([](SkGlyph* g) { + if (g->fPathData) { + delete g->fPathData->fPath; + } } ); SkDescriptor::Free(fDesc); delete fScalerContext; this->invokeAndRemoveAuxProcs(); @@ -214,14 +217,185 @@ const void* SkGlyphCache::findImage(const SkGlyph& glyph) { const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { if (glyph.fWidth) { - if (glyph.fPath == nullptr) { - const_cast(glyph).fPath = new SkPath; - fScalerContext->getPath(glyph, glyph.fPath); - fMemoryUsed += sizeof(SkPath) + - glyph.fPath->countPoints() * sizeof(SkPoint); + if (glyph.fPathData == nullptr) { + SkGlyph::PathData* pathData = + (SkGlyph::PathData* ) fGlyphAlloc.allocThrow(sizeof(SkGlyph::PathData)); + const_cast(glyph).fPathData = pathData; + pathData->fIntercept = nullptr; + SkPath* path = pathData->fPath = new SkPath; + fScalerContext->getPath(glyph, path); + fMemoryUsed += sizeof(SkPath) + path->countPoints() * sizeof(SkPoint); } } - return glyph.fPath; + return glyph.fPathData ? glyph.fPathData->fPath : nullptr; +} + +#include "../pathops/SkPathOpsCubic.h" +#include "../pathops/SkPathOpsQuad.h" + +static bool quad_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) { + SkScalar min = SkTMin(SkTMin(pts[0], pts[2]), pts[4]); + if (bounds[1] < min) { + return false; + } + SkScalar max = SkTMax(SkTMax(pts[0], pts[2]), pts[4]); + return bounds[0] < max; +} + +static bool cubic_in_bounds(const SkScalar* pts, const SkScalar bounds[2]) { + SkScalar min = SkTMin(SkTMin(SkTMin(pts[0], pts[2]), pts[4]), pts[6]); + if (bounds[1] < min) { + return false; + } + SkScalar max = SkTMax(SkTMax(SkTMax(pts[0], pts[2]), pts[4]), pts[6]); + return bounds[0] < max; +} + +void SkGlyphCache::OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale, + SkScalar xPos, SkScalar* array, int* count) { + if (array) { + array += *count; + for (int index = 0; index < 2; index++) { + *array++ = intercept->fInterval[index] * scale + xPos; + } + } + *count += 2; +} + +void SkGlyphCache::AddInterval(SkScalar val, SkGlyph::Intercept* intercept) { + intercept->fInterval[0] = SkTMin(intercept->fInterval[0], val); + intercept->fInterval[1] = SkTMax(intercept->fInterval[1], val); +} + +void SkGlyphCache::AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2], + bool yAxis, SkGlyph::Intercept* intercept) { + for (int i = 0; i < ptCount; ++i) { + SkScalar val = *(&pts[i].fY - yAxis); + if (bounds[0] < val && val < bounds[1]) { + AddInterval(*(&pts[i].fX + yAxis), intercept); + } + } +} + +void SkGlyphCache::AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis, + SkGlyph::Intercept* intercept) { + SkScalar t = yAxis ? (axis - pts[0].fX) / (pts[1].fX - pts[0].fX) + : (axis - pts[0].fY) / (pts[1].fY - pts[0].fY); + if (0 <= t && t < 1) { // this handles divide by zero above + AddInterval(yAxis ? pts[0].fY + t * (pts[1].fY - pts[0].fY) + : pts[0].fX + t * (pts[1].fX - pts[0].fX), intercept); + } +} + +void SkGlyphCache::AddQuad(const SkPoint pts[2], SkScalar axis, bool yAxis, + SkGlyph::Intercept* intercept) { + SkDQuad quad; + quad.set(pts); + double roots[2]; + int count = yAxis ? quad.verticalIntersect(axis, roots) + : quad.horizontalIntersect(axis, roots); + while (--count >= 0) { + SkPoint pt = quad.ptAtT(roots[count]).asSkPoint(); + AddInterval(*(&pt.fX + yAxis), intercept); + } +} + +void SkGlyphCache::AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis, + SkGlyph::Intercept* intercept) { + SkDCubic cubic; + cubic.set(pts); + double roots[3]; + int count = yAxis ? cubic.verticalIntersect(axis, roots) + : cubic.horizontalIntersect(axis, roots); + while (--count >= 0) { + SkPoint pt = cubic.ptAtT(roots[count]).asSkPoint(); + AddInterval(*(&pt.fX + yAxis), intercept); + } +} + +const SkGlyph::Intercept* SkGlyphCache::MatchBounds(const SkGlyph* glyph, + const SkScalar bounds[2]) { + if (!glyph->fPathData) { + return nullptr; + } + const SkGlyph::Intercept* intercept = glyph->fPathData->fIntercept; + while (intercept) { + if (bounds[0] == intercept->fBounds[0] && bounds[1] == intercept->fBounds[1]) { + return intercept; + } + intercept = intercept->fNext; + } + return nullptr; +} + +void SkGlyphCache::findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, + bool yAxis, SkGlyph* glyph, SkScalar* array, int* count) { + const SkGlyph::Intercept* match = MatchBounds(glyph, bounds); + + if (match) { + if (match->fInterval[0] < match->fInterval[1]) { + OffsetResults(match, scale, xPos, array, count); + } + return; + } + + SkGlyph::Intercept* intercept = + (SkGlyph::Intercept* ) fGlyphAlloc.allocThrow(sizeof(SkGlyph::Intercept)); + intercept->fNext = glyph->fPathData->fIntercept; + intercept->fBounds[0] = bounds[0]; + intercept->fBounds[1] = bounds[1]; + intercept->fInterval[0] = SK_ScalarMax; + intercept->fInterval[1] = SK_ScalarMin; + glyph->fPathData->fIntercept = intercept; + const SkPath* path = glyph->fPathData->fPath; + const SkRect& pathBounds = path->getBounds(); + if (*(&pathBounds.fBottom - yAxis) < bounds[0] || bounds[1] < *(&pathBounds.fTop - yAxis)) { + return; + } + SkPath::Iter iter(*path, false); + SkPoint pts[4]; + SkPath::Verb verb; + while (SkPath::kDone_Verb != (verb = iter.next(pts))) { + switch (verb) { + case SkPath::kMove_Verb: + break; + case SkPath::kLine_Verb: + AddLine(pts, bounds[0], yAxis, intercept); + AddLine(pts, bounds[1], yAxis, intercept); + AddPoints(pts, 2, bounds, yAxis, intercept); + break; + case SkPath::kQuad_Verb: + if (!quad_in_bounds(&pts[0].fY - yAxis, bounds)) { + break; + } + AddQuad(pts, bounds[0], yAxis, intercept); + AddQuad(pts, bounds[1], yAxis, intercept); + AddPoints(pts, 3, bounds, yAxis, intercept); + break; + case SkPath::kConic_Verb: + SkASSERT(0); // no support for text composed of conics + break; + case SkPath::kCubic_Verb: + if (!cubic_in_bounds(&pts[0].fY - yAxis, bounds)) { + break; + } + AddCubic(pts, bounds[0], yAxis, intercept); + AddCubic(pts, bounds[1], yAxis, intercept); + AddPoints(pts, 4, bounds, yAxis, intercept); + break; + case SkPath::kClose_Verb: + break; + default: + SkASSERT(0); + break; + } + } + if (intercept->fInterval[0] >= intercept->fInterval[1]) { + intercept->fInterval[0] = SK_ScalarMax; + intercept->fInterval[1] = SK_ScalarMin; + return; + } + OffsetResults(intercept, scale, xPos, array, count); } void SkGlyphCache::dump() const { diff --git a/gfx/skia/skia/src/core/SkGlyphCache.h b/gfx/skia/skia/src/core/SkGlyphCache.h index 2f1f417250..2b5262ba5e 100644 --- a/gfx/skia/skia/src/core/SkGlyphCache.h +++ b/gfx/skia/skia/src/core/SkGlyphCache.h @@ -11,12 +11,12 @@ #include "SkChunkAlloc.h" #include "SkDescriptor.h" #include "SkGlyph.h" +#include "SkPaint.h" #include "SkTHash.h" #include "SkScalerContext.h" #include "SkTemplates.h" #include "SkTDArray.h" -class SkPaint; class SkTraceMemoryDump; class SkGlyphCache_Globals; @@ -77,6 +77,12 @@ public: */ const void* findImage(const SkGlyph&); + /** If the advance axis intersects the glyph's path, append the positions scaled and offset + to the array (if non-null), and set the count to the updated array length. + */ + void findIntercepts(const SkScalar bounds[2], SkScalar scale, SkScalar xPos, + bool yAxis, SkGlyph* , SkScalar* array, int* count); + /** Return the Path associated with the glyph. If it has not been generated this will trigger that. */ @@ -131,6 +137,7 @@ public: the global cache list (after which the caller should not reference it anymore. */ static void AttachCache(SkGlyphCache*); + using AttachCacheFunctor = SkFunctionWrapper; /** Detach a strike from the global cache matching the specified descriptor. Once detached, it can be queried/modified by the current thread, and when finished, be reattached to the @@ -232,6 +239,20 @@ private: inline static SkGlyphCache* FindTail(SkGlyphCache* head); + static void OffsetResults(const SkGlyph::Intercept* intercept, SkScalar scale, + SkScalar xPos, SkScalar* array, int* count); + static void AddInterval(SkScalar val, SkGlyph::Intercept* intercept); + static void AddPoints(const SkPoint* pts, int ptCount, const SkScalar bounds[2], + bool yAxis, SkGlyph::Intercept* intercept); + static void AddLine(const SkPoint pts[2], SkScalar axis, bool yAxis, + SkGlyph::Intercept* intercept); + static void AddQuad(const SkPoint pts[2], SkScalar axis, bool yAxis, + SkGlyph::Intercept* intercept); + static void AddCubic(const SkPoint pts[3], SkScalar axis, bool yAxis, + SkGlyph::Intercept* intercept); + static const SkGlyph::Intercept* MatchBounds(const SkGlyph* glyph, + const SkScalar bounds[2]); + SkGlyphCache* fNext; SkGlyphCache* fPrev; SkDescriptor* const fDesc; @@ -251,74 +272,40 @@ private: AuxProcRec* fAuxProcList; }; -class SkAutoGlyphCacheBase { +class SkAutoGlyphCache : public std::unique_ptr { public: - SkGlyphCache* getCache() const { return fCache; } + /** deprecated: use get() */ + SkGlyphCache* getCache() const { return this->get(); } - void release() { - if (fCache) { - SkGlyphCache::AttachCache(fCache); - fCache = nullptr; - } - } - -protected: - // Hide the constructors so we can't create one of these directly. Create SkAutoGlyphCache or - // SkAutoGlyphCacheNoCache instead. - SkAutoGlyphCacheBase(SkGlyphCache* cache) : fCache(cache) {} - SkAutoGlyphCacheBase(SkTypeface* typeface, const SkDescriptor* desc) { - fCache = SkGlyphCache::DetachCache(typeface, desc); - } - SkAutoGlyphCacheBase(const SkPaint& /*paint*/, - const SkSurfaceProps* /*surfaceProps*/, - const SkMatrix* /*matrix*/) { - fCache = nullptr; - } - SkAutoGlyphCacheBase() { - fCache = nullptr; - } - ~SkAutoGlyphCacheBase() { - if (fCache) { - SkGlyphCache::AttachCache(fCache); - } - } - - SkGlyphCache* fCache; - -private: - static bool DetachProc(const SkGlyphCache*, void*); -}; - -class SkAutoGlyphCache : public SkAutoGlyphCacheBase { -public: - SkAutoGlyphCache(SkGlyphCache* cache) : SkAutoGlyphCacheBase(cache) {} - SkAutoGlyphCache(SkTypeface* typeface, const SkDescriptor* desc) : - SkAutoGlyphCacheBase(typeface, desc) {} + SkAutoGlyphCache(SkGlyphCache* cache) : INHERITED(cache) {} + SkAutoGlyphCache(SkTypeface* typeface, const SkDescriptor* desc) + : INHERITED(SkGlyphCache::DetachCache(typeface, desc)) + {} + /** deprecated: always enables fake gamma */ SkAutoGlyphCache(const SkPaint& paint, const SkSurfaceProps* surfaceProps, - const SkMatrix* matrix) { - fCache = paint.detachCache(surfaceProps, matrix, false); - } - + const SkMatrix* matrix) + : INHERITED(paint.detachCache(surfaceProps, SkPaint::FakeGamma::On, matrix)) + {} + SkAutoGlyphCache(const SkPaint& paint, + const SkSurfaceProps* surfaceProps, + SkPaint::FakeGamma fakeGamma, + const SkMatrix* matrix) + : INHERITED(paint.detachCache(surfaceProps, fakeGamma, matrix)) + {} private: - SkAutoGlyphCache() : SkAutoGlyphCacheBase() {} + using INHERITED = std::unique_ptr; }; -#define SkAutoGlyphCache(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCache) -class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCacheBase { +class SkAutoGlyphCacheNoGamma : public SkAutoGlyphCache { public: - SkAutoGlyphCacheNoGamma(SkGlyphCache* cache) : SkAutoGlyphCacheBase(cache) {} - SkAutoGlyphCacheNoGamma(SkTypeface* typeface, const SkDescriptor* desc) : - SkAutoGlyphCacheBase(typeface, desc) {} SkAutoGlyphCacheNoGamma(const SkPaint& paint, const SkSurfaceProps* surfaceProps, - const SkMatrix* matrix) { - fCache = paint.detachCache(surfaceProps, matrix, true); - } - -private: - SkAutoGlyphCacheNoGamma() : SkAutoGlyphCacheBase() {} + const SkMatrix* matrix) + : SkAutoGlyphCache(paint, surfaceProps, SkPaint::FakeGamma::Off, matrix) + {} }; +#define SkAutoGlyphCache(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCache) #define SkAutoGlyphCacheNoGamma(...) SK_REQUIRE_LOCAL_VAR(SkAutoGlyphCacheNoGamma) #endif diff --git a/gfx/skia/skia/src/core/SkGraphics.cpp b/gfx/skia/skia/src/core/SkGraphics.cpp index 5290ea1d07..0273139ad7 100644 --- a/gfx/skia/skia/src/core/SkGraphics.cpp +++ b/gfx/skia/skia/src/core/SkGraphics.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2006 The Android Open Source Project * @@ -13,6 +12,7 @@ #include "SkCanvas.h" #include "SkGeometry.h" #include "SkGlyphCache.h" +#include "SkImageFilter.h" #include "SkMath.h" #include "SkMatrix.h" #include "SkOpts.h" @@ -65,6 +65,12 @@ void SkGraphics::DumpMemoryStatistics(SkTraceMemoryDump* dump) { SkGlyphCache::DumpMemoryStatistics(dump); } +void SkGraphics::PurgeAllCaches() { + SkGraphics::PurgeFontCache(); + SkGraphics::PurgeResourceCache(); + SkImageFilter::PurgeCache(); +} + /////////////////////////////////////////////////////////////////////////////// static const char kFontCacheLimitStr[] = "font-cache-limit"; diff --git a/gfx/skia/skia/src/core/SkHalf.cpp b/gfx/skia/skia/src/core/SkHalf.cpp index 0db979bc76..262362e076 100644 --- a/gfx/skia/skia/src/core/SkHalf.cpp +++ b/gfx/skia/skia/src/core/SkHalf.cpp @@ -34,18 +34,18 @@ SkHalf SkFloatToHalf(float f) { static const uint32_t sign_mask = 0x80000000u; static const uint32_t round_mask = ~0xfffu; SkHalf o = 0; - + FloatUIntUnion floatUnion; floatUnion.fFloat = f; - + uint32_t sign = floatUnion.fUInt & sign_mask; floatUnion.fUInt ^= sign; - + // NOTE all the integer compares in this function can be safely // compiled into signed compares since all operands are below // 0x80000000. Important if you want fast straight SSE2 code // (since there's no unsigned PCMPGTD). - + // Inf or NaN (all exponent bits set) if (floatUnion.fUInt >= f32infty) // NaN->qNaN and Inf->Inf @@ -59,10 +59,10 @@ SkHalf SkFloatToHalf(float f) { if (floatUnion.fUInt > f16infty) { floatUnion.fUInt = f16infty; } - + o = floatUnion.fUInt >> 13; // Take the bits! } - + o |= sign >> 16; return o; } @@ -72,7 +72,7 @@ SkHalf SkFloatToHalf(float f) { float SkHalfToFloat(SkHalf h) { static const FloatUIntUnion magic = { 126 << 23 }; FloatUIntUnion o; - + if (halfExponent(h) == 0) { // Zero / Denormal @@ -90,7 +90,7 @@ float SkHalfToFloat(SkHalf h) { else o.fUInt |= ((127 - 15 + halfExponent(h)) << 23); } - + // Set sign o.fUInt |= (halfSign(h) << 31); return o.fFloat; diff --git a/gfx/skia/skia/src/core/SkHalf.h b/gfx/skia/skia/src/core/SkHalf.h index 7e41c6ff0c..5f5575ae1a 100644 --- a/gfx/skia/skia/src/core/SkHalf.h +++ b/gfx/skia/skia/src/core/SkHalf.h @@ -8,6 +8,7 @@ #ifndef SkHalf_DEFINED #define SkHalf_DEFINED +#include "SkNx.h" #include "SkTypes.h" // 16-bit floating point value @@ -23,4 +24,101 @@ typedef uint16_t SkHalf; float SkHalfToFloat(SkHalf h); SkHalf SkFloatToHalf(float f); +// Convert between half and single precision floating point, but pull any dirty +// trick we can to make it faster as long as it's correct enough for values in [0,1]. +static inline Sk4f SkHalfToFloat_01(uint64_t); +static inline uint64_t SkFloatToHalf_01(const Sk4f&); + +// ~~~~~~~~~~~ impl ~~~~~~~~~~~~~~ // + +// Like the serial versions in SkHalf.cpp, these are based on +// https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/ + +// GCC 4.9 lacks the intrinsics to use ARMv8 f16<->f32 instructions, so we use inline assembly. + +static inline Sk4f SkHalfToFloat_01(uint64_t hs) { +#if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64) + float32x4_t fs; + asm ("fmov %d[fs], %[hs] \n" // vcreate_f16(hs) + "fcvtl %[fs].4s, %[fs].4h \n" // vcvt_f32_f16(...) + : [fs] "=w" (fs) // =w: write-only NEON register + : [hs] "r" (hs)); // r: read-only 64-bit general register + return fs; + +#elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON) + // NEON makes this pretty easy: + // - denormals are 10-bit * 2^-14 == 24-bit fixed point; + // - handle normals the same way as in SSE: align mantissa, then rebias exponent. + uint32x4_t h = vmovl_u16(vcreate_u16(hs)), + is_denorm = vcltq_u32(h, vdupq_n_u32(1<<10)); + float32x4_t denorm = vcvtq_n_f32_u32(h, 24), + norm = vreinterpretq_f32_u32(vaddq_u32(vshlq_n_u32(h, 13), + vdupq_n_u32((127-15) << 23))); + return vbslq_f32(is_denorm, denorm, norm); + +#elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 + // If our input is a normal 16-bit float, things are pretty easy: + // - shift left by 13 to put the mantissa in the right place; + // - the exponent is wrong, but it just needs to be rebiased; + // - re-bias the exponent from 15-bias to 127-bias by adding (127-15). + + // If our input is denormalized, we're going to do the same steps, plus a few more fix ups: + // - the input is h = K*2^-14, for some 10-bit fixed point K in [0,1); + // - by shifting left 13 and adding (127-15) to the exponent, we constructed the float value + // 2^-15*(1+K); + // - we'd need to subtract 2^-15 and multiply by 2 to get back to K*2^-14, or equivallently + // multiply by 2 then subtract 2^-14. + // + // - We'll work that multiply by 2 into the rebias, by adding 1 more to the exponent. + // - Conveniently, this leaves that rebias constant 2^-14, exactly what we want to subtract. + + __m128i h = _mm_unpacklo_epi16(_mm_loadl_epi64((const __m128i*)&hs), _mm_setzero_si128()); + const __m128i is_denorm = _mm_cmplt_epi32(h, _mm_set1_epi32(1<<10)); + + __m128i rebias = _mm_set1_epi32((127-15) << 23); + rebias = _mm_add_epi32(rebias, _mm_and_si128(is_denorm, _mm_set1_epi32(1<<23))); + + __m128i f = _mm_add_epi32(_mm_slli_epi32(h, 13), rebias); + return _mm_sub_ps(_mm_castsi128_ps(f), + _mm_castsi128_ps(_mm_and_si128(is_denorm, rebias))); +#else + float fs[4]; + for (int i = 0; i < 4; i++) { + fs[i] = SkHalfToFloat(hs >> (i*16)); + } + return Sk4f::Load(fs); +#endif +} + +static inline uint64_t SkFloatToHalf_01(const Sk4f& fs) { + uint64_t r; +#if !defined(SKNX_NO_SIMD) && defined(SK_CPU_ARM64) + float32x4_t vec = fs.fVec; + asm ("fcvtn %[vec].4h, %[vec].4s \n" // vcvt_f16_f32(vec) + "fmov %[r], %d[vec] \n" // vst1_f16(&r, ...) + : [r] "=r" (r) // =r: write-only 64-bit general register + , [vec] "+w" (vec)); // +w: read-write NEON register + +// TODO: ARMv7 NEON float->half? + +#elif !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 + // Scale down from 127-bias to 15-bias, then cut off bottom 13 mantissa bits. + // This doesn't round, so it can be 1 bit too small. + const __m128 rebias = _mm_castsi128_ps(_mm_set1_epi32((127 - (127-15)) << 23)); + __m128i h = _mm_srli_epi32(_mm_castps_si128(_mm_mul_ps(fs.fVec, rebias)), 13); + _mm_storel_epi64((__m128i*)&r, _mm_packs_epi32(h,h)); + +#else + SkHalf hs[4]; + for (int i = 0; i < 4; i++) { + hs[i] = SkFloatToHalf(fs[i]); + } + r = (uint64_t)hs[3] << 48 + | (uint64_t)hs[2] << 32 + | (uint64_t)hs[1] << 16 + | (uint64_t)hs[0] << 0; +#endif + return r; +} + #endif diff --git a/gfx/skia/skia/src/core/SkImageCacherator.cpp b/gfx/skia/skia/src/core/SkImageCacherator.cpp index 572778e19c..7b5ff22677 100644 --- a/gfx/skia/skia/src/core/SkImageCacherator.cpp +++ b/gfx/skia/skia/src/core/SkImageCacherator.cpp @@ -55,7 +55,7 @@ SkImageCacherator* SkImageCacherator::NewFromGenerator(SkImageGenerator* gen, // Now that we know we can hand-off the generator (to be owned by the cacherator) we can // release our holder. (we DONT want to delete it here anymore) - genHolder.detach(); + genHolder.release(); return new SkImageCacherator(gen, gen->getInfo().makeWH(subset->width(), subset->height()), SkIPoint::Make(subset->x(), subset->y()), uniqueID); @@ -171,7 +171,8 @@ bool SkImageCacherator::lockAsBitmap(SkBitmap* bitmap, const SkImage* client, } const uint32_t pixelOpsFlags = 0; - if (!tex->readPixels(0, 0, bitmap->width(), bitmap->height(), SkImageInfo2GrPixelConfig(fInfo), + if (!tex->readPixels(0, 0, bitmap->width(), bitmap->height(), + SkImageInfo2GrPixelConfig(fInfo, *tex->getContext()->caps()), bitmap->getPixels(), bitmap->rowBytes(), pixelOpsFlags)) { bitmap->reset(); return false; @@ -203,7 +204,7 @@ static GrTexture* load_compressed_into_texture(GrContext* ctx, SkData* data, GrS } desc.fConfig = config; - return ctx->textureProvider()->createTexture(desc, true, rawStart, 0); + return ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes, rawStart, 0); } class Generator_GrYUVProvider : public GrYUVProvider { @@ -213,12 +214,11 @@ public: Generator_GrYUVProvider(SkImageGenerator* gen) : fGen(gen) {} uint32_t onGetID() override { return fGen->uniqueID(); } - bool onGetYUVSizes(SkISize sizes[3]) override { - return fGen->getYUV8Planes(sizes, nullptr, nullptr, nullptr); + bool onQueryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const override { + return fGen->queryYUV8(sizeInfo, colorSpace); } - bool onGetYUVPlanes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* space) override { - return fGen->getYUV8Planes(sizes, planes, rowBytes, space); + bool onGetYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) override { + return fGen->getYUV8Planes(sizeInfo, planes); } }; @@ -239,10 +239,26 @@ static GrTexture* set_key_and_return(GrTexture* tex, const GrUniqueKey& key) { * 5. Ask the generator to return RGB(A) data, which the GPU can convert */ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key, - const SkImage* client, SkImage::CachingHint chint) { + const SkImage* client, SkImage::CachingHint chint, + bool willBeMipped) { + // Values representing the various texture lock paths we can take. Used for logging the path + // taken to a histogram. + enum LockTexturePath { + kFailure_LockTexturePath, + kPreExisting_LockTexturePath, + kNative_LockTexturePath, + kCompressed_LockTexturePath, + kYUV_LockTexturePath, + kRGBA_LockTexturePath, + }; + + enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 }; + // 1. Check the cache for a pre-existing one if (key.isValid()) { if (GrTexture* tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(key)) { + SK_HISTOGRAM_ENUMERATION("LockTexturePath", kPreExisting_LockTexturePath, + kLockTexturePathCount); return tex; } } @@ -252,17 +268,21 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key ScopedGenerator generator(this); SkIRect subset = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), fInfo.width(), fInfo.height()); if (GrTexture* tex = generator->generateTexture(ctx, &subset)) { + SK_HISTOGRAM_ENUMERATION("LockTexturePath", kNative_LockTexturePath, + kLockTexturePathCount); return set_key_and_return(tex, key); } } - const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(fInfo); + const GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(fInfo, *ctx->caps()); // 3. Ask the generator to return a compressed form that the GPU might support SkAutoTUnref data(this->refEncoded(ctx)); if (data) { GrTexture* tex = load_compressed_into_texture(ctx, data, desc); if (tex) { + SK_HISTOGRAM_ENUMERATION("LockTexturePath", kCompressed_LockTexturePath, + kLockTexturePathCount); return set_key_and_return(tex, key); } } @@ -273,6 +293,8 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key Generator_GrYUVProvider provider(generator); GrTexture* tex = provider.refAsTexture(ctx, desc, true); if (tex) { + SK_HISTOGRAM_ENUMERATION("LockTexturePath", kYUV_LockTexturePath, + kLockTexturePathCount); return set_key_and_return(tex, key); } } @@ -280,11 +302,21 @@ GrTexture* SkImageCacherator::lockTexture(GrContext* ctx, const GrUniqueKey& key // 5. Ask the generator to return RGB(A) data, which the GPU can convert SkBitmap bitmap; if (this->tryLockAsBitmap(&bitmap, client, chint)) { - GrTexture* tex = GrUploadBitmapToTexture(ctx, bitmap); + GrTexture* tex = nullptr; + if (willBeMipped) { + tex = GrGenerateMipMapsAndUploadToTexture(ctx, bitmap); + } + if (!tex) { + tex = GrUploadBitmapToTexture(ctx, bitmap); + } if (tex) { + SK_HISTOGRAM_ENUMERATION("LockTexturePath", kRGBA_LockTexturePath, + kLockTexturePathCount); return set_key_and_return(tex, key); } } + SK_HISTOGRAM_ENUMERATION("LockTexturePath", kFailure_LockTexturePath, + kLockTexturePathCount); return nullptr; } diff --git a/gfx/skia/skia/src/core/SkImageCacherator.h b/gfx/skia/skia/src/core/SkImageCacherator.h index 6b000668dd..7e146182c7 100644 --- a/gfx/skia/skia/src/core/SkImageCacherator.h +++ b/gfx/skia/skia/src/core/SkImageCacherator.h @@ -75,7 +75,7 @@ private: // Returns the texture. If the cacherator is generating the texture and wants to cache it, // it should use the passed in key (if the key is valid). GrTexture* lockTexture(GrContext*, const GrUniqueKey& key, const SkImage* client, - SkImage::CachingHint); + SkImage::CachingHint, bool willBeMipped); #endif class ScopedGenerator { diff --git a/gfx/skia/skia/src/core/SkImageFilter.cpp b/gfx/skia/skia/src/core/SkImageFilter.cpp index 6fd098d2f7..743dc2ad1a 100644 --- a/gfx/skia/skia/src/core/SkImageFilter.cpp +++ b/gfx/skia/skia/src/core/SkImageFilter.cpp @@ -6,6 +6,7 @@ */ #include "SkImageFilter.h" +#include "SkImageFilterCacheKey.h" #include "SkBitmap.h" #include "SkBitmapDevice.h" @@ -13,12 +14,12 @@ #include "SkDevice.h" #include "SkLocalMatrixImageFilter.h" #include "SkMatrixImageFilter.h" -#include "SkMutex.h" #include "SkOncePtr.h" #include "SkReadBuffer.h" #include "SkRect.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" #include "SkTDynamicHash.h" -#include "SkTHash.h" #include "SkTInternalLList.h" #include "SkValidationUtils.h" #include "SkWriteBuffer.h" @@ -66,34 +67,42 @@ void SkImageFilter::CropRect::toString(SkString* str) const { } #endif -bool SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds, const Context& ctx, +void SkImageFilter::CropRect::applyTo(const SkIRect& imageBounds, + const SkMatrix& ctm, + bool embiggen, SkIRect* cropped) const { *cropped = imageBounds; if (fFlags) { SkRect devCropR; - ctx.ctm().mapRect(&devCropR, fRect); - const SkIRect devICropR = devCropR.roundOut(); + ctm.mapRect(&devCropR, fRect); + SkIRect devICropR = devCropR.roundOut(); - // Compute the left/top first, in case we have to read them to compute right/bottom + // Compute the left/top first, in case we need to modify the right/bottom for a missing edge if (fFlags & kHasLeft_CropEdge) { - cropped->fLeft = devICropR.fLeft; + if (embiggen || devICropR.fLeft > cropped->fLeft) { + cropped->fLeft = devICropR.fLeft; + } + } else { + devICropR.fRight = cropped->fLeft + devICropR.width(); } if (fFlags & kHasTop_CropEdge) { - cropped->fTop = devICropR.fTop; + if (embiggen || devICropR.fTop > cropped->fTop) { + cropped->fTop = devICropR.fTop; + } + } else { + devICropR.fBottom = cropped->fTop + devICropR.height(); } if (fFlags & kHasWidth_CropEdge) { - cropped->fRight = cropped->fLeft + devICropR.width(); + if (embiggen || devICropR.fRight < cropped->fRight) { + cropped->fRight = devICropR.fRight; + } } if (fFlags & kHasHeight_CropEdge) { - cropped->fBottom = cropped->fTop + devICropR.height(); + if (embiggen || devICropR.fBottom < cropped->fBottom) { + cropped->fBottom = devICropR.fBottom; + } } } - // Intersect against the clip bounds, in case the crop rect has - // grown the bounds beyond the original clip. This can happen for - // example in tiling, where the clip is much smaller than the filtered - // primitive. If we didn't do this, we would be processing the filter - // at the full crop rect size in every tile. - return cropped->intersect(ctx.clipBounds()); } /////////////////////////////////////////////////////////////////////////////////////////////////// @@ -109,42 +118,14 @@ static int32_t next_image_filter_unique_id() { return id; } -struct SkImageFilter::Cache::Key { - Key(const uint32_t uniqueID, const SkMatrix& matrix, const SkIRect& clipBounds, uint32_t srcGenID) - : fUniqueID(uniqueID), fMatrix(matrix), fClipBounds(clipBounds), fSrcGenID(srcGenID) { - // Assert that Key is tightly-packed, since it is hashed. - static_assert(sizeof(Key) == sizeof(uint32_t) + sizeof(SkMatrix) + sizeof(SkIRect) + - sizeof(uint32_t), "image_filter_key_tight_packing"); - fMatrix.getType(); // force initialization of type, so hashes match - } - uint32_t fUniqueID; - SkMatrix fMatrix; - SkIRect fClipBounds; - uint32_t fSrcGenID; - bool operator==(const Key& other) const { - return fUniqueID == other.fUniqueID - && fMatrix == other.fMatrix - && fClipBounds == other.fClipBounds - && fSrcGenID == other.fSrcGenID; - } -}; - -SkImageFilter::Common::~Common() { - for (int i = 0; i < fInputs.count(); ++i) { - SkSafeUnref(fInputs[i]); - } -} - void SkImageFilter::Common::allocInputs(int count) { - const size_t size = count * sizeof(SkImageFilter*); fInputs.reset(count); - sk_bzero(fInputs.get(), size); } void SkImageFilter::Common::detachInputs(SkImageFilter** inputs) { - const size_t size = fInputs.count() * sizeof(SkImageFilter*); - memcpy(inputs, fInputs.get(), size); - sk_bzero(fInputs.get(), size); + for (int i = 0; i < fInputs.count(); ++i) { + inputs[i] = fInputs[i].release(); + } } bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) { @@ -159,7 +140,7 @@ bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) { this->allocInputs(count); for (int i = 0; i < count; i++) { if (buffer.readBool()) { - fInputs[i] = buffer.readImageFilter(); + fInputs[i] = sk_sp(buffer.readImageFilter()); } if (!buffer.isValid()) { return false; @@ -182,6 +163,22 @@ bool SkImageFilter::Common::unflatten(SkReadBuffer& buffer, int expectedCount) { /////////////////////////////////////////////////////////////////////////////////////////////////// +SkImageFilter::SkImageFilter(sk_sp* inputs, + int inputCount, + const CropRect* cropRect) + : fInputCount(inputCount), + fInputs(new SkImageFilter*[inputCount]), + fUsesSrcInput(false), + fCropRect(cropRect ? *cropRect : CropRect(SkRect(), 0x0)), + fUniqueID(next_image_filter_unique_id()) { + for (int i = 0; i < inputCount; ++i) { + if (nullptr == inputs[i] || inputs[i]->usesSrcInput()) { + fUsesSrcInput = true; + } + fInputs[i] = SkSafeRef(inputs[i].get()); + } +} + SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropRect* cropRect) : fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]), @@ -192,8 +189,7 @@ SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs, const CropR if (nullptr == inputs[i] || inputs[i]->usesSrcInput()) { fUsesSrcInput = true; } - fInputs[i] = inputs[i]; - SkSafeRef(fInputs[i]); + fInputs[i] = SkSafeRef(inputs[i]); } } @@ -202,7 +198,7 @@ SkImageFilter::~SkImageFilter() { SkSafeUnref(fInputs[i]); } delete[] fInputs; - Cache::Get()->purgeByImageFilterId(fUniqueID); + Cache::Get()->purgeByKeys(fCacheKeys.begin(), fCacheKeys.count()); } SkImageFilter::SkImageFilter(int inputCount, SkReadBuffer& buffer) @@ -238,13 +234,38 @@ void SkImageFilter::flatten(SkWriteBuffer& buffer) const { buffer.writeUInt(fCropRect.flags()); } -bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src, - const Context& context, - SkBitmap* result, SkIPoint* offset) const { +sk_sp SkImageFilter::filterImage(SkSpecialImage* src, const Context& context, + SkIPoint* offset) const { + SkASSERT(src && offset); + + uint32_t srcGenID = fUsesSrcInput ? src->uniqueID() : 0; + const SkIRect srcSubset = fUsesSrcInput ? src->subset() : SkIRect::MakeWH(0, 0); + Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID, srcSubset); + if (context.cache()) { + SkSpecialImage* result = context.cache()->get(key, offset); + if (result) { + return sk_sp(SkRef(result)); + } + } + + sk_sp result(this->onFilterImage(src, context, offset)); + if (result && context.cache()) { + context.cache()->set(key, result.get(), *offset); + SkAutoMutexAcquire mutex(fMutex); + fCacheKeys.push_back(key); + } + + return result; +} + +bool SkImageFilter::filterImageDeprecated(Proxy* proxy, const SkBitmap& src, + const Context& context, + SkBitmap* result, SkIPoint* offset) const { SkASSERT(result); SkASSERT(offset); uint32_t srcGenID = fUsesSrcInput ? src.getGenerationID() : 0; - Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(), srcGenID); + Cache::Key key(fUniqueID, context.ctm(), context.clipBounds(), + srcGenID, SkIRect::MakeWH(0, 0)); if (context.cache()) { if (context.cache()->get(key, result, offset)) { return true; @@ -255,53 +276,75 @@ bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src, * the filter to do it. */ if ((proxy && proxy->filterImage(this, src, context, result, offset)) || - this->onFilterImage(proxy, src, context, result, offset)) { + this->onFilterImageDeprecated(proxy, src, context, result, offset)) { if (context.cache()) { context.cache()->set(key, *result, *offset); + SkAutoMutexAcquire mutex(fMutex); + fCacheKeys.push_back(key); } return true; } return false; } -bool SkImageFilter::filterInput(int index, Proxy* proxy, const SkBitmap& src, - const Context& ctx, - SkBitmap* result, SkIPoint* offset) const { +bool SkImageFilter::filterInputDeprecated(int index, Proxy* proxy, const SkBitmap& src, + const Context& ctx, + SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; } - return input->filterImage(proxy, src, this->mapContext(ctx), result, offset); -} -bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst) const { - SkASSERT(dst); - return this->onFilterBounds(src, ctm, dst); -} - -void SkImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { - if (0 == fInputCount) { - *dst = src; - return; + // SRGBTODO: Don't handle sRGB here, in anticipation of this code path being deleted. + sk_sp specialSrc(SkSpecialImage::internal_fromBM(proxy, src, nullptr)); + if (!specialSrc) { + return false; } - if (this->getInput(0)) { - this->getInput(0)->computeFastBounds(src, dst); + + sk_sp tmp(input->onFilterImage(specialSrc.get(), + this->mapContext(ctx), + offset)); + if (!tmp) { + return false; + } + + return tmp->internal_getBM(result); +} + +SkIRect SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm, + MapDirection direction) const { + if (kReverse_MapDirection == direction) { + SkIRect bounds = this->onFilterNodeBounds(src, ctm, direction); + return this->onFilterBounds(bounds, ctm, direction); } else { - *dst = src; + SkIRect bounds = this->onFilterBounds(src, ctm, direction); + bounds = this->onFilterNodeBounds(bounds, ctm, direction); + SkIRect dst; + this->getCropRect().applyTo(bounds, ctm, this->affectsTransparentBlack(), &dst); + return dst; } +} + +SkRect SkImageFilter::computeFastBounds(const SkRect& src) const { + if (0 == fInputCount) { + return src; + } + SkRect combinedBounds = this->getInput(0) ? this->getInput(0)->computeFastBounds(src) : src; for (int i = 1; i < fInputCount; i++) { SkImageFilter* input = this->getInput(i); if (input) { - SkRect bounds; - input->computeFastBounds(src, &bounds); - dst->join(bounds); + combinedBounds.join(input->computeFastBounds(src)); } else { - dst->join(src); + combinedBounds.join(src); } } + return combinedBounds; } bool SkImageFilter::canComputeFastBounds() const { + if (this->affectsTransparentBlack()) { + return false; + } for (int i = 0; i < fInputCount; i++) { SkImageFilter* input = this->getInput(i); if (input && !input->canComputeFastBounds()) { @@ -311,27 +354,49 @@ bool SkImageFilter::canComputeFastBounds() const { return true; } -bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const Context&, - SkBitmap*, SkIPoint*) const { +bool SkImageFilter::onFilterImageDeprecated(Proxy*, const SkBitmap&, const Context&, + SkBitmap*, SkIPoint*) const { + // Only classes that now use the new SkSpecialImage-based path will not have + // onFilterImageDeprecated methods. For those classes we should never be + // calling this method. + SkASSERT(0); return false; } +// SkImageFilter-derived classes that do not yet have their own onFilterImage +// implementation convert back to calling the deprecated filterImage method +sk_sp SkImageFilter::onFilterImage(SkSpecialImage* src, const Context& ctx, + SkIPoint* offset) const { + SkBitmap srcBM, resultBM; + + if (!src->internal_getBM(&srcBM)) { + return nullptr; + } + + // This is the only valid call to the old filterImage path + if (!this->filterImageDeprecated(src->internal_getProxy(), srcBM, ctx, &resultBM, offset)) { + return nullptr; + } + + return SkSpecialImage::internal_fromBM(src->internal_getProxy(), resultBM, &src->props()); +} + bool SkImageFilter::canFilterImageGPU() const { return this->asFragmentProcessor(nullptr, nullptr, SkMatrix::I(), SkIRect()); } -bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx, - SkBitmap* result, SkIPoint* offset) const { +bool SkImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, + SkBitmap* result, SkIPoint* offset) const { #if SK_SUPPORT_GPU SkBitmap input = src; SkASSERT(fInputCount == 1); SkIPoint srcOffset = SkIPoint::Make(0, 0); - if (!this->filterInputGPU(0, proxy, src, ctx, &input, &srcOffset)) { + if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &input, &srcOffset)) { return false; } GrTexture* srcTexture = input.getTexture(); SkIRect bounds; - if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) { + if (!this->applyCropRectDeprecated(ctx, proxy, input, &srcOffset, &bounds, &input)) { return false; } GrContext* context = srcTexture->getContext(); @@ -354,6 +419,7 @@ bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Cont SkMatrix matrix(ctx.ctm()); matrix.postTranslate(SkIntToScalar(-bounds.left()), SkIntToScalar(-bounds.top())); GrPaint paint; + // SRGBTODO: Don't handle sRGB here, in anticipation of this code path being deleted. if (this->asFragmentProcessor(&fp, srcTexture, matrix, bounds)) { SkASSERT(fp); paint.addColorFragmentProcessor(fp)->unref(); @@ -386,35 +452,27 @@ bool SkImageFilter::asAColorFilter(SkColorFilter** filterPtr) const { return true; } -bool SkImageFilter::applyCropRect(const Context& ctx, const SkBitmap& src, - const SkIPoint& srcOffset, SkIRect* dstBounds, - SkIRect* srcBounds) const { - SkIRect storage; - if (!srcBounds) { - srcBounds = &storage; - } - src.getBounds(srcBounds); - srcBounds->offset(srcOffset); -#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS - return fCropRect.applyTo(*srcBounds, ctx, dstBounds); -#else - this->onFilterNodeBounds(*srcBounds, ctx.ctm(), dstBounds, kForward_MapDirection); - return fCropRect.applyTo(*dstBounds, ctx, dstBounds); -#endif +bool SkImageFilter::applyCropRect(const Context& ctx, const SkIRect& srcBounds, + SkIRect* dstBounds) const { + SkIRect temp = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection); + fCropRect.applyTo(temp, ctx.ctm(), this->affectsTransparentBlack(), dstBounds); + // Intersect against the clip bounds, in case the crop rect has + // grown the bounds beyond the original clip. This can happen for + // example in tiling, where the clip is much smaller than the filtered + // primitive. If we didn't do this, we would be processing the filter + // at the full crop rect size in every tile. + return dstBounds->intersect(ctx.clipBounds()); } -bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src, - SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const { +bool SkImageFilter::applyCropRectDeprecated(const Context& ctx, Proxy* proxy, const SkBitmap& src, + SkIPoint* srcOffset, SkIRect* bounds, + SkBitmap* dst) const { SkIRect srcBounds; src.getBounds(&srcBounds); srcBounds.offset(*srcOffset); -#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS - if (!fCropRect.applyTo(srcBounds, ctx, bounds)) { -#else - SkIRect dstBounds; - this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection); - if (!fCropRect.applyTo(dstBounds, ctx, bounds)) { -#endif + SkIRect dstBounds = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection); + fCropRect.applyTo(dstBounds, ctx.ctm(), this->affectsTransparentBlack(), bounds); + if (!bounds->intersect(ctx.clipBounds())) { return false; } @@ -435,21 +493,62 @@ bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitm } } -bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, - SkIRect* dst) const { - if (fInputCount < 1) { - *dst = src; - return true; +// Return a larger (newWidth x newHeight) copy of 'src' with black padding +// around it. +static sk_sp pad_image(SkSpecialImage* src, + int newWidth, int newHeight, int offX, int offY) { + + SkImageInfo info = SkImageInfo::MakeN32Premul(newWidth, newHeight); + sk_sp surf(src->makeSurface(info)); + if (!surf) { + return nullptr; } - SkIRect bounds, totalBounds; - this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection); + SkCanvas* canvas = surf->getCanvas(); + SkASSERT(canvas); + + canvas->clear(0x0); + + src->draw(canvas, offX, offY, nullptr); + + return surf->makeImageSnapshot(); +} + +sk_sp SkImageFilter::applyCropRect(const Context& ctx, + SkSpecialImage* src, + SkIPoint* srcOffset, + SkIRect* bounds) const { + SkIRect srcBounds; + srcBounds = SkIRect::MakeXYWH(srcOffset->fX, srcOffset->fY, src->width(), src->height()); + + SkIRect dstBounds = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection); + fCropRect.applyTo(dstBounds, ctx.ctm(), this->affectsTransparentBlack(), bounds); + if (!bounds->intersect(ctx.clipBounds())) { + return nullptr; + } + + if (srcBounds.contains(*bounds)) { + return sk_sp(SkRef(src)); + } else { + sk_sp img(pad_image(src, + bounds->width(), bounds->height(), + srcOffset->x() - bounds->x(), + srcOffset->y() - bounds->y())); + *srcOffset = SkIPoint::Make(bounds->x(), bounds->y()); + return img; + } +} + +SkIRect SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, + MapDirection direction) const { + if (fInputCount < 1) { + return src; + } + + SkIRect totalBounds; for (int i = 0; i < fInputCount; ++i) { SkImageFilter* filter = this->getInput(i); - SkIRect rect = bounds; - if (filter && !filter->filterBounds(bounds, ctm, &rect)) { - return false; - } + SkIRect rect = filter ? filter->filterBounds(src, ctm, direction) : src; if (0 == i) { totalBounds = rect; } else { @@ -457,27 +556,18 @@ bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm, } } - // don't modify dst until now, so we don't accidentally change it in the - // loop, but then return false on the next filter. - *dst = totalBounds; - return true; + return totalBounds; } -void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, - SkIRect* dst, MapDirection) const { - *dst = src; +SkIRect SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&, MapDirection) const { + return src; } SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const { -#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS - return ctx; -#else - SkIRect clipBounds; - this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds, - MapDirection::kReverse_MapDirection); + SkIRect clipBounds = this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), + MapDirection::kReverse_MapDirection); return Context(ctx.ctm(), clipBounds, ctx.cache()); -#endif } bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, @@ -485,49 +575,86 @@ bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*, return false; } -SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix, - SkFilterQuality filterQuality, - SkImageFilter* input) { - return SkMatrixImageFilter::Create(matrix, filterQuality, input); +sk_sp SkImageFilter::MakeMatrixFilter(const SkMatrix& matrix, + SkFilterQuality filterQuality, + sk_sp input) { + return SkMatrixImageFilter::Make(matrix, filterQuality, std::move(input)); } -SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const { +sk_sp SkImageFilter::makeWithLocalMatrix(const SkMatrix& matrix) const { // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter // is *always* treated as a const ptr. Hence the const-cast here. // - return SkLocalMatrixImageFilter::Create(matrix, const_cast(this)); + SkImageFilter* nonConstThis = const_cast(this); + return SkLocalMatrixImageFilter::Make(matrix, sk_ref_sp(nonConstThis)); +} + +sk_sp SkImageFilter::filterInput(int index, + SkSpecialImage* src, + const Context& ctx, + SkIPoint* offset) const { + SkImageFilter* input = this->getInput(index); + if (!input) { + return sk_sp(SkRef(src)); + } + + sk_sp result(input->filterImage(src, this->mapContext(ctx), offset)); + +#if SK_SUPPORT_GPU + if (src->peekTexture() && result && !result->peekTexture()) { + // Keep the result on the GPU - this is still required for some + // image filters that don't support GPU in all cases + GrContext* context = src->peekTexture()->getContext(); + return result->makeTextureImage(src->internal_getProxy(), context); + } +#endif + + return result; } #if SK_SUPPORT_GPU -bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy, - const SkBitmap& src, const Context& ctx, - SkBitmap* result, SkIPoint* offset) const { +bool SkImageFilter::filterInputGPUDeprecated(int index, SkImageFilter::Proxy* proxy, + const SkBitmap& src, const Context& ctx, + SkBitmap* result, SkIPoint* offset) const { SkImageFilter* input = this->getInput(index); if (!input) { return true; } - // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity - // matrix with no clip and that the matrix, clip, and render target set before this function was - // called are restored before we return to the caller. - GrContext* context = src.getTexture()->getContext(); - if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) { - if (!result->getTexture()) { - const SkImageInfo info = result->info(); - if (kUnknown_SkColorType == info.colorType()) { - return false; - } - SkAutoTUnref resultTex( - GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter())); - if (!resultTex) { - return false; - } - result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); - } - return true; - } else { + + // SRGBTODO: Don't handle sRGB here, in anticipation of this code path being deleted. + sk_sp specialSrc(SkSpecialImage::internal_fromBM(proxy, src, nullptr)); + if (!specialSrc) { return false; } + + sk_sp tmp(input->onFilterImage(specialSrc.get(), + this->mapContext(ctx), + offset)); + if (!tmp) { + return false; + } + + if (!tmp->internal_getBM(result)) { + return false; + } + + if (!result->getTexture()) { + GrContext* context = src.getTexture()->getContext(); + + const SkImageInfo info = result->info(); + if (kUnknown_SkColorType == info.colorType()) { + return false; + } + SkAutoTUnref resultTex( + GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter())); + if (!resultTex) { + return false; + } + result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref(); + } + + return true; } #endif @@ -535,9 +662,8 @@ namespace { class CacheImpl : public SkImageFilter::Cache { public: - CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) { - } - virtual ~CacheImpl() { + CacheImpl(size_t maxBytes) : fMaxBytes(maxBytes), fCurrentBytes(0) { } + ~CacheImpl() override { SkTDynamicHash::Iter iter(&fLookup); while (!iter.done()) { @@ -545,13 +671,16 @@ public: ++iter; delete v; } - fIdToKeys.foreach([](uint32_t, SkTArray** array) { delete *array; }); } struct Value { Value(const Key& key, const SkBitmap& bitmap, const SkIPoint& offset) : fKey(key), fBitmap(bitmap), fOffset(offset) {} + Value(const Key& key, SkSpecialImage* image, const SkIPoint& offset) + : fKey(key), fImage(SkRef(image)), fOffset(offset) {} + Key fKey; SkBitmap fBitmap; + SkAutoTUnref fImage; SkIPoint fOffset; static const Key& GetKey(const Value& v) { return v.fKey; @@ -561,6 +690,7 @@ public: } SK_DECLARE_INTERNAL_LLIST_INTERFACE(Value); }; + bool get(const Key& key, SkBitmap* result, SkIPoint* offset) const override { SkAutoMutexAcquire mutex(fMutex); if (Value* v = fLookup.find(key)) { @@ -574,19 +704,26 @@ public: } return false; } + + SkSpecialImage* get(const Key& key, SkIPoint* offset) const override { + SkAutoMutexAcquire mutex(fMutex); + if (Value* v = fLookup.find(key)) { + *offset = v->fOffset; + if (v != fLRU.head()) { + fLRU.remove(v); + fLRU.addToHead(v); + } + return v->fImage; + } + return nullptr; + } + void set(const Key& key, const SkBitmap& result, const SkIPoint& offset) override { SkAutoMutexAcquire mutex(fMutex); if (Value* v = fLookup.find(key)) { - removeInternal(v); + this->removeInternal(v); } Value* v = new Value(key, result, offset); - if (SkTArray** array = fIdToKeys.find(key.fUniqueID)) { - (*array)->push_back(key); - } else { - SkTArray* keyArray = new SkTArray(); - keyArray->push_back(key); - fIdToKeys.set(key.fUniqueID, keyArray); - } fLookup.add(v); fLRU.addToHead(v); fCurrentBytes += result.getSize(); @@ -596,7 +733,26 @@ public: if (tail == v) { break; } - removeInternal(tail); + this->removeInternal(tail); + } + } + + void set(const Key& key, SkSpecialImage* image, const SkIPoint& offset) override { + SkAutoMutexAcquire mutex(fMutex); + if (Value* v = fLookup.find(key)) { + this->removeInternal(v); + } + Value* v = new Value(key, image, offset); + fLookup.add(v); + fLRU.addToHead(v); + fCurrentBytes += image->getSize(); + while (fCurrentBytes > fMaxBytes) { + Value* tail = fLRU.tail(); + SkASSERT(tail); + if (tail == v) { + break; + } + this->removeInternal(tail); } } @@ -609,29 +765,28 @@ public: } } - void purgeByImageFilterId(uint32_t uniqueID) override { + void purgeByKeys(const Key keys[], int count) override { SkAutoMutexAcquire mutex(fMutex); - if (SkTArray** array = fIdToKeys.find(uniqueID)) { - for (auto& key : **array) { - if (Value* v = fLookup.find(key)) { - this->removeInternal(v); - } + for (int i = 0; i < count; i++) { + if (Value* v = fLookup.find(keys[i])) { + this->removeInternal(v); } - fIdToKeys.remove(uniqueID); - delete *array; // This can be deleted outside the lock } } private: void removeInternal(Value* v) { - fCurrentBytes -= v->fBitmap.getSize(); + if (v->fImage) { + fCurrentBytes -= v->fImage->getSize(); + } else { + fCurrentBytes -= v->fBitmap.getSize(); + } fLRU.remove(v); fLookup.remove(v->fKey); delete v; } private: SkTDynamicHash fLookup; - SkTHashMap*> fIdToKeys; mutable SkTInternalLList fLRU; size_t fMaxBytes; size_t fCurrentBytes; @@ -676,4 +831,3 @@ bool SkImageFilter::DeviceProxy::filterImage(const SkImageFilter* filter, const SkBitmap* result, SkIPoint* offset) { return fDevice->filterImage(filter, src, ctx, result, offset); } - diff --git a/gfx/skia/skia/src/core/SkImageFilterCacheKey.h b/gfx/skia/skia/src/core/SkImageFilterCacheKey.h new file mode 100644 index 0000000000..3f5d1c8fa2 --- /dev/null +++ b/gfx/skia/skia/src/core/SkImageFilterCacheKey.h @@ -0,0 +1,41 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkImageFilterCacheKey_DEFINED +#define SkImageFilterCacheKey_DEFINED + +struct SkImageFilter::Cache::Key { + Key(const uint32_t uniqueID, const SkMatrix& matrix, + const SkIRect& clipBounds, uint32_t srcGenID, const SkIRect& srcSubset) + : fUniqueID(uniqueID) + , fMatrix(matrix) + , fClipBounds(clipBounds) + , fSrcGenID(srcGenID) + , fSrcSubset(srcSubset) { + // Assert that Key is tightly-packed, since it is hashed. + static_assert(sizeof(Key) == sizeof(uint32_t) + sizeof(SkMatrix) + sizeof(SkIRect) + + sizeof(uint32_t) + 4 * sizeof(int32_t), + "image_filter_key_tight_packing"); + fMatrix.getType(); // force initialization of type, so hashes match + } + + uint32_t fUniqueID; + SkMatrix fMatrix; + SkIRect fClipBounds; + uint32_t fSrcGenID; + SkIRect fSrcSubset; + + bool operator==(const Key& other) const { + return fUniqueID == other.fUniqueID && + fMatrix == other.fMatrix && + fClipBounds == other.fClipBounds && + fSrcGenID == other.fSrcGenID && + fSrcSubset == other.fSrcSubset; + } +}; + +#endif diff --git a/gfx/skia/skia/src/core/SkImageGenerator.cpp b/gfx/skia/skia/src/core/SkImageGenerator.cpp index 7d71b6701c..c8c94c2270 100644 --- a/gfx/skia/skia/src/core/SkImageGenerator.cpp +++ b/gfx/skia/skia/src/core/SkImageGenerator.cpp @@ -52,56 +52,28 @@ bool SkImageGenerator::getPixels(const SkImageInfo& info, void* pixels, size_t r return this->getPixels(info, pixels, rowBytes, nullptr, nullptr); } -bool SkImageGenerator::getYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) { -#ifdef SK_DEBUG - // In all cases, we need the sizes array - SkASSERT(sizes); +bool SkImageGenerator::queryYUV8(SkYUVSizeInfo* sizeInfo, SkYUVColorSpace* colorSpace) const { + SkASSERT(sizeInfo); - bool isValidWithPlanes = (planes) && (rowBytes) && - ((planes[0]) && (planes[1]) && (planes[2]) && - (0 != rowBytes[0]) && (0 != rowBytes[1]) && (0 != rowBytes[2])); - bool isValidWithoutPlanes = - ((nullptr == planes) || - ((nullptr == planes[0]) && (nullptr == planes[1]) && (nullptr == planes[2]))) && - ((nullptr == rowBytes) || - ((0 == rowBytes[0]) && (0 == rowBytes[1]) && (0 == rowBytes[2]))); - - // Either we have all planes and rowBytes information or we have none of it - // Having only partial information is not supported - SkASSERT(isValidWithPlanes || isValidWithoutPlanes); - - // If we do have planes information, make sure all sizes are non 0 - // and all rowBytes are valid - SkASSERT(!isValidWithPlanes || - ((sizes[0].fWidth >= 0) && - (sizes[0].fHeight >= 0) && - (sizes[1].fWidth >= 0) && - (sizes[1].fHeight >= 0) && - (sizes[2].fWidth >= 0) && - (sizes[2].fHeight >= 0) && - (rowBytes[0] >= (size_t)sizes[0].fWidth) && - (rowBytes[1] >= (size_t)sizes[1].fWidth) && - (rowBytes[2] >= (size_t)sizes[2].fWidth))); -#endif - - return this->onGetYUV8Planes(sizes, planes, rowBytes, colorSpace); + return this->onQueryYUV8(sizeInfo, colorSpace); } -bool SkImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3]) { - return false; -} +bool SkImageGenerator::getYUV8Planes(const SkYUVSizeInfo& sizeInfo, void* planes[3]) { + SkASSERT(sizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth >= 0); + SkASSERT(sizeInfo.fSizes[SkYUVSizeInfo::kY].fHeight >= 0); + SkASSERT(sizeInfo.fSizes[SkYUVSizeInfo::kU].fWidth >= 0); + SkASSERT(sizeInfo.fSizes[SkYUVSizeInfo::kU].fHeight >= 0); + SkASSERT(sizeInfo.fSizes[SkYUVSizeInfo::kV].fWidth >= 0); + SkASSERT(sizeInfo.fSizes[SkYUVSizeInfo::kV].fHeight >= 0); + SkASSERT(sizeInfo.fWidthBytes[SkYUVSizeInfo::kY] >= + (size_t) sizeInfo.fSizes[SkYUVSizeInfo::kY].fWidth); + SkASSERT(sizeInfo.fWidthBytes[SkYUVSizeInfo::kU] >= + (size_t) sizeInfo.fSizes[SkYUVSizeInfo::kU].fWidth); + SkASSERT(sizeInfo.fWidthBytes[SkYUVSizeInfo::kV] >= + (size_t) sizeInfo.fSizes[SkYUVSizeInfo::kV].fWidth); + SkASSERT(planes && planes[0] && planes[1] && planes[2]); -bool SkImageGenerator::onGetYUV8Planes(SkISize sizes[3], void* planes[3], size_t rowBytes[3], - SkYUVColorSpace* colorSpace) { - // In order to maintain compatibility with clients that implemented the original - // onGetYUV8Planes interface, we assume that the color space is JPEG. - // TODO(rileya): remove this and the old onGetYUV8Planes once clients switch over to - // the new interface. - if (colorSpace) { - *colorSpace = kJPEG_SkYUVColorSpace; - } - return this->onGetYUV8Planes(sizes, planes, rowBytes); + return this->onGetYUV8Planes(sizeInfo, planes); } GrTexture* SkImageGenerator::generateTexture(GrContext* ctx, const SkIRect* subset) { diff --git a/gfx/skia/skia/src/core/SkImageInfo.cpp b/gfx/skia/skia/src/core/SkImageInfo.cpp index b90f858562..16f0a0109c 100644 --- a/gfx/skia/skia/src/core/SkImageInfo.cpp +++ b/gfx/skia/skia/src/core/SkImageInfo.cpp @@ -61,6 +61,7 @@ bool SkColorTypeValidateAlphaType(SkColorType colorType, SkAlphaType alphaType, case kARGB_4444_SkColorType: case kRGBA_8888_SkColorType: case kBGRA_8888_SkColorType: + case kRGBA_F16_SkColorType: if (kUnknown_SkAlphaType == alphaType) { return false; } @@ -120,4 +121,3 @@ bool SkReadPixelsRec::trim(int srcWidth, int srcHeight) { return true; } - diff --git a/gfx/skia/skia/src/core/SkLayerInfo.h b/gfx/skia/skia/src/core/SkLayerInfo.h index 04ae1794a2..aa19ecbd0c 100644 --- a/gfx/skia/skia/src/core/SkLayerInfo.h +++ b/gfx/skia/skia/src/core/SkLayerInfo.h @@ -9,6 +9,8 @@ #define SkLayerInfo_DEFINED #include "SkBigPicture.h" +#include "SkMatrix.h" +#include "SkPaint.h" #include "SkTArray.h" // This class stores information about the saveLayer/restore pairs found diff --git a/gfx/skia/skia/src/core/SkLight.h b/gfx/skia/skia/src/core/SkLight.h index 4a6e149c76..d9eb78d112 100644 --- a/gfx/skia/skia/src/core/SkLight.h +++ b/gfx/skia/skia/src/core/SkLight.h @@ -1,4 +1,3 @@ - /* * Copyright 2015 Google Inc. * @@ -40,9 +39,9 @@ public: LightType type() const { return fType; } const SkColor3f& color() const { return fColor; } - const SkVector3& dir() const { + const SkVector3& dir() const { SkASSERT(kAmbient_LightType != fType); - return fDirection; + return fDirection; } private: diff --git a/gfx/skia/skia/src/core/SkLightingShader.cpp b/gfx/skia/skia/src/core/SkLightingShader.cpp index a9fcac3b83..eba7d652eb 100644 --- a/gfx/skia/skia/src/core/SkLightingShader.cpp +++ b/gfx/skia/skia/src/core/SkLightingShader.cpp @@ -1,4 +1,3 @@ - /* * Copyright 2015 Google Inc. * @@ -53,7 +52,7 @@ public: SkLightingShaderImpl(const SkBitmap& diffuse, const SkBitmap& normal, const SkLightingShader::Lights* lights, const SkVector& invNormRotation, - const SkMatrix* diffLocalM, const SkMatrix* normLocalM) + const SkMatrix* diffLocalM, const SkMatrix* normLocalM) : INHERITED(diffLocalM) , fDiffuseMap(diffuse) , fNormalMap(normal) @@ -79,8 +78,6 @@ public: SkFilterQuality) const override; #endif - size_t contextSize() const override; - class LightingShaderContext : public SkShader::Context { public: // The context takes ownership of the states. It will call their destructors @@ -106,6 +103,7 @@ public: protected: void flatten(SkWriteBuffer&) const override; + size_t onContextSize(const ContextRec&) const override; Context* onCreateContext(const ContextRec&, void*) const override; bool computeNormTotalInverse(const ContextRec& rec, SkMatrix* normTotalInverse) const; @@ -185,34 +183,34 @@ public: // add uniforms const char* lightDirUniName = nullptr; - fLightDirUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + fLightDirUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec3f_GrSLType, kDefault_GrSLPrecision, "LightDir", &lightDirUniName); const char* lightColorUniName = nullptr; - fLightColorUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + fLightColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec3f_GrSLType, kDefault_GrSLPrecision, "LightColor", &lightColorUniName); const char* ambientColorUniName = nullptr; - fAmbientColorUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec3f_GrSLType, kDefault_GrSLPrecision, "AmbientColor", &ambientColorUniName); const char* xformUniName = nullptr; - fXformUni = uniformHandler->addUniform(GrGLSLUniformHandler::kFragment_Visibility, + fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kVec2f_GrSLType, kDefault_GrSLPrecision, "Xform", &xformUniName); fragBuilder->codeAppend("vec4 diffuseColor = "); - fragBuilder->appendTextureLookupAndModulate(args.fInputColor, args.fSamplers[0], - args.fCoords[0].c_str(), + fragBuilder->appendTextureLookupAndModulate(args.fInputColor, args.fSamplers[0], + args.fCoords[0].c_str(), args.fCoords[0].getType()); fragBuilder->codeAppend(";"); fragBuilder->codeAppend("vec4 normalColor = "); fragBuilder->appendTextureLookup(args.fSamplers[1], - args.fCoords[1].c_str(), + args.fCoords[1].c_str(), args.fCoords[1].getType()); fragBuilder->codeAppend(";"); @@ -221,7 +219,7 @@ public: fragBuilder->codeAppendf( "mat3 m = mat3(%s.x, -%s.y, 0.0, %s.y, %s.x, 0.0, 0.0, 0.0, 1.0);", xformUniName, xformUniName, xformUniName, xformUniName); - + // TODO: inverse map the light direction vectors in the vertex shader rather than // transforming all the normals here! fragBuilder->codeAppend("normal = normalize(m*normal);"); @@ -303,7 +301,7 @@ public: private: GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new LightingGLFP; } - bool onIsEqual(const GrFragmentProcessor& proc) const override { + bool onIsEqual(const GrFragmentProcessor& proc) const override { const LightingFP& lightingFP = proc.cast(); return fDiffDeviceTransform == lightingFP.fDiffDeviceTransform && fNormDeviceTransform == lightingFP.fNormDeviceTransform && @@ -332,7 +330,7 @@ static bool make_mat(const SkBitmap& bm, const SkMatrix& localMatrix1, const SkMatrix* localMatrix2, SkMatrix* result) { - + result->setIDiv(bm.width(), bm.height()); SkMatrix lmInverse; @@ -361,7 +359,7 @@ const GrFragmentProcessor* SkLightingShaderImpl::asFragmentProcessor( SkASSERT(fDiffuseMap.width() == fNormalMap.width() && fDiffuseMap.height() == fNormalMap.height()); SkMatrix diffM, normM; - + if (!make_mat(fDiffuseMap, this->getLocalMatrix(), localMatrix, &diffM)) { return nullptr; } @@ -372,17 +370,17 @@ const GrFragmentProcessor* SkLightingShaderImpl::asFragmentProcessor( bool doBicubic; GrTextureParams::FilterMode diffFilterMode = GrSkFilterQualityToGrFilterMode( - SkTMin(filterQuality, kMedium_SkFilterQuality), + SkTMin(filterQuality, kMedium_SkFilterQuality), viewM, this->getLocalMatrix(), - &doBicubic); + &doBicubic); SkASSERT(!doBicubic); GrTextureParams::FilterMode normFilterMode = GrSkFilterQualityToGrFilterMode( - SkTMin(filterQuality, kMedium_SkFilterQuality), + SkTMin(filterQuality, kMedium_SkFilterQuality), viewM, fNormLocalMatrix, - &doBicubic); + &doBicubic); SkASSERT(!doBicubic); // TODO: support other tile modes @@ -416,10 +414,6 @@ bool SkLightingShaderImpl::isOpaque() const { return fDiffuseMap.isOpaque(); } -size_t SkLightingShaderImpl::contextSize() const { - return 2 * sizeof(SkBitmapProcState) + sizeof(LightingShaderContext); -} - SkLightingShaderImpl::LightingShaderContext::LightingShaderContext(const SkLightingShaderImpl& shader, const ContextRec& rec, SkBitmapProcState* diffuseState, @@ -452,19 +446,19 @@ static inline SkPMColor convert(SkColor3f color, U8CPU a) { color.fX = 0.0f; } else if (color.fX >= 255.0f) { color.fX = 255.0f; - } + } if (color.fY <= 0.0f) { color.fY = 0.0f; } else if (color.fY >= 255.0f) { color.fY = 255.0f; - } + } if (color.fZ <= 0.0f) { color.fZ = 0.0f; } else if (color.fZ >= 255.0f) { color.fZ = 255.0f; - } + } return SkPreMultiplyARGB(a, (int) color.fX, (int) color.fY, (int) color.fZ); } @@ -516,7 +510,7 @@ void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y, xformedNorm.fX = lightShader.fInvNormRotation.fX * norm.fX + lightShader.fInvNormRotation.fY * norm.fY; - xformedNorm.fY = lightShader.fInvNormRotation.fX * norm.fX - + xformedNorm.fY = lightShader.fInvNormRotation.fX * norm.fX - lightShader.fInvNormRotation.fY * norm.fY; xformedNorm.fZ = norm.fZ; @@ -558,7 +552,7 @@ void SkLightingShaderImpl::toString(SkString* str) const { } #endif -SkFlattenable* SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { +sk_sp SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { SkMatrix diffLocalM; bool hasDiffLocalM = buf.readBool(); if (hasDiffLocalM) { @@ -606,14 +600,14 @@ SkFlattenable* SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) { if (!buf.readScalarArray(&dir.fX, 3)) { return nullptr; } - builder.add(SkLight(color, dir)); + builder.add(SkLight(color, dir)); } } SkAutoTUnref lights(builder.finish()); - return new SkLightingShaderImpl(diffuse, normal, lights, SkVector::Make(1.0f, 0.0f), - &diffLocalM, &normLocalM); + return sk_make_sp(diffuse, normal, lights, SkVector::Make(1.0f, 0.0f), + &diffLocalM, &normLocalM); } void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const { @@ -655,6 +649,10 @@ bool SkLightingShaderImpl::computeNormTotalInverse(const ContextRec& rec, return m->invert(normTotalInverse); } +size_t SkLightingShaderImpl::onContextSize(const ContextRec&) const { + return 2 * sizeof(SkBitmapProcState) + sizeof(LightingShaderContext); +} + SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, void* storage) const { @@ -671,7 +669,7 @@ SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, SkBitmapProcState* diffuseState = new (diffuseStateStorage) SkBitmapProcState(fDiffuseMap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); SkASSERT(diffuseState); - if (!diffuseState->chooseProcs(diffTotalInv, *rec.fPaint)) { + if (!diffuseState->setup(diffTotalInv, *rec.fPaint)) { diffuseState->~SkBitmapProcState(); return nullptr; } @@ -680,7 +678,7 @@ SkShader::Context* SkLightingShaderImpl::onCreateContext(const ContextRec& rec, SkBitmapProcState* normalState = new (normalStateStorage) SkBitmapProcState(fNormalMap, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode); SkASSERT(normalState); - if (!normalState->chooseProcs(normTotalInv, *rec.fPaint)) { + if (!normalState->setup(normTotalInv, *rec.fPaint)) { diffuseState->~SkBitmapProcState(); normalState->~SkBitmapProcState(); return nullptr; @@ -701,10 +699,10 @@ static bool bitmap_is_too_big(const SkBitmap& bm) { return bm.width() > kMaxSize || bm.height() > kMaxSize; } -SkShader* SkLightingShader::Create(const SkBitmap& diffuse, const SkBitmap& normal, - const Lights* lights, - const SkVector& invNormRotation, - const SkMatrix* diffLocalM, const SkMatrix* normLocalM) { +sk_sp SkLightingShader::Make(const SkBitmap& diffuse, const SkBitmap& normal, + const Lights* lights, + const SkVector& invNormRotation, + const SkMatrix* diffLocalM, const SkMatrix* normLocalM) { if (diffuse.isNull() || bitmap_is_too_big(diffuse) || normal.isNull() || bitmap_is_too_big(normal) || diffuse.width() != normal.width() || @@ -714,8 +712,8 @@ SkShader* SkLightingShader::Create(const SkBitmap& diffuse, const SkBitmap& norm SkASSERT(SkScalarNearlyEqual(invNormRotation.lengthSqd(), SK_Scalar1)); - return new SkLightingShaderImpl(diffuse, normal, lights, invNormRotation, diffLocalM, - normLocalM); + return sk_make_sp(diffuse, normal, lights, invNormRotation, diffLocalM, + normLocalM); } /////////////////////////////////////////////////////////////////////////////// diff --git a/gfx/skia/skia/src/core/SkLightingShader.h b/gfx/skia/skia/src/core/SkLightingShader.h index f87db31edb..ffbcbe9eb4 100644 --- a/gfx/skia/skia/src/core/SkLightingShader.h +++ b/gfx/skia/skia/src/core/SkLightingShader.h @@ -1,4 +1,3 @@ - /* * Copyright 2015 Google Inc. * @@ -6,7 +5,6 @@ * found in the LICENSE file. */ - #ifndef SkLightingShader_DEFINED #define SkLightingShader_DEFINED @@ -38,7 +36,7 @@ public: } const Lights* finish() { - return fLights.detach(); + return fLights.release(); } private: @@ -71,7 +69,7 @@ public: @param normal the normal map @param light the light applied to the normal map @param ambient the linear (unpremul) ambient light color. Range is 0..1/channel. - @param localMatrix the matrix mapping the textures to the dest rect + @param localMatrix the matrix mapping the textures to the dest rect nullptr will be returned if: either 'diffuse' or 'normal' are empty @@ -90,9 +88,9 @@ public: The +Z axis is thus encoded in RGB as (127, 127, 255) while the -Z axis is (127, 127, 0). */ - static SkShader* Create(const SkBitmap& diffuse, const SkBitmap& normal, - const Lights* lights, const SkVector& invNormRotation, - const SkMatrix* diffLocalMatrix, const SkMatrix* normLocalMatrix); + static sk_sp Make(const SkBitmap& diffuse, const SkBitmap& normal, + const Lights* lights, const SkVector& invNormRotation, + const SkMatrix* diffLocalMatrix, const SkMatrix* normLocalMatrix); SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() }; diff --git a/gfx/skia/skia/src/core/SkLineClipper.h b/gfx/skia/skia/src/core/SkLineClipper.h index 11e0a73ca2..d2c9b5fe76 100644 --- a/gfx/skia/skia/src/core/SkLineClipper.h +++ b/gfx/skia/skia/src/core/SkLineClipper.h @@ -1,4 +1,3 @@ - /* * Copyright 2011 Google Inc. * diff --git a/gfx/skia/skia/src/core/SkLinearBitmapPipeline.cpp b/gfx/skia/skia/src/core/SkLinearBitmapPipeline.cpp new file mode 100644 index 0000000000..f4133b01ac --- /dev/null +++ b/gfx/skia/skia/src/core/SkLinearBitmapPipeline.cpp @@ -0,0 +1,652 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkLinearBitmapPipeline.h" + +#include "SkPM4f.h" +#include +#include +#include +#include "SkColor.h" +#include "SkSize.h" + +#ifdef MOZ_SKIA +#include "mozilla/Tuple.h" + +namespace std { + using mozilla::Tie; + using mozilla::Tuple; + #define tie Tie + #define tuple Tuple +} +#else +#include +#endif + +#include "SkLinearBitmapPipeline_core.h" +#include "SkLinearBitmapPipeline_matrix.h" +#include "SkLinearBitmapPipeline_tile.h" +#include "SkLinearBitmapPipeline_sample.h" + +class SkLinearBitmapPipeline::PointProcessorInterface { +public: + virtual ~PointProcessorInterface() { } + // Take the first n (where 0 < n && n < 4) items from xs and ys and sample those points. For + // nearest neighbor, that means just taking the floor xs and ys. For bilerp, this means + // to expand the bilerp filter around the point and sample using that filter. + virtual void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0; + // Same as pointListFew, but n = 4. + virtual void VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0; + // A span is a compact form of sample points that are obtained by mapping points from + // destination space to source space. This is used for horizontal lines only, and is mainly + // used to take advantage of memory coherence for horizontal spans. + virtual void pointSpan(Span span) = 0; +}; + +class SkLinearBitmapPipeline::SampleProcessorInterface + : public SkLinearBitmapPipeline::PointProcessorInterface { +public: + // Used for nearest neighbor when scale factor is 1.0. The span can just be repeated with no + // edge pixel alignment problems. This is for handling a very common case. + virtual void repeatSpan(Span span, int32_t repeatCount) = 0; + + // The x's and y's are setup in the following order: + // +--------+--------+ + // | | | + // | px00 | px10 | + // | 0 | 1 | + // +--------+--------+ + // | | | + // | px01 | px11 | + // | 2 | 3 | + // +--------+--------+ + // These pixels coordinates are arranged in the following order in xs and ys: + // px00 px10 px01 px11 + virtual void VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) = 0; + + // A span represents sample points that have been mapped from destination space to source + // space. Each sample point is then expanded to the four bilerp points by add +/- 0.5. The + // resulting Y values my be off the tile. When y +/- 0.5 are more than 1 apart because of + // tiling, the second Y is used to denote the retiled Y value. + virtual void bilerpSpan(Span span, SkScalar y) = 0; +}; + +class SkLinearBitmapPipeline::PixelPlacerInterface { +public: + virtual ~PixelPlacerInterface() { } + // Count is normally not needed, but in these early stages of development it is useful to + // check bounds. + // TODO(herb): 4/6/2016 - remove count when code is stable. + virtual void setDestination(void* dst, int count) = 0; + virtual void VECTORCALL placePixel(Sk4f pixel0) = 0; + virtual void VECTORCALL place4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) = 0; +}; + +namespace { + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Matrix Stage +// PointProcessor uses a strategy to help complete the work of the different stages. The strategy +// must implement the following methods: +// * processPoints(xs, ys) - must mutate the xs and ys for the stage. +// * maybeProcessSpan(span, next) - This represents a horizontal series of pixels +// to work over. +// span - encapsulation of span. +// next - a pointer to the next stage. +// maybeProcessSpan - returns false if it can not process the span and needs to fallback to +// point lists for processing. +template +class MatrixStage final : public SkLinearBitmapPipeline::PointProcessorInterface { +public: + template + MatrixStage(Next* next, Args&&... args) + : fNext{next} + , fStrategy{std::forward(args)...}{ } + + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { + fStrategy.processPoints(&xs, &ys); + fNext->pointListFew(n, xs, ys); + } + + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { + fStrategy.processPoints(&xs, &ys); + fNext->pointList4(xs, ys); + } + + // The span you pass must not be empty. + void pointSpan(Span span) override { + SkASSERT(!span.isEmpty()); + if (!fStrategy.maybeProcessSpan(span, fNext)) { + span_fallback(span, this); + } + } + +private: + Next* const fNext; + Strategy fStrategy; +}; + +template +using TranslateMatrix = MatrixStage; + +template +using ScaleMatrix = MatrixStage; + +template +using AffineMatrix = MatrixStage; + +template +using PerspectiveMatrix = MatrixStage; + + +static SkLinearBitmapPipeline::PointProcessorInterface* choose_matrix( + SkLinearBitmapPipeline::PointProcessorInterface* next, + const SkMatrix& inverse, + SkLinearBitmapPipeline::MatrixStage* matrixProc) { + if (inverse.hasPerspective()) { + matrixProc->Initialize>( + next, + SkVector{inverse.getTranslateX(), inverse.getTranslateY()}, + SkVector{inverse.getScaleX(), inverse.getScaleY()}, + SkVector{inverse.getSkewX(), inverse.getSkewY()}, + SkVector{inverse.getPerspX(), inverse.getPerspY()}, + inverse.get(SkMatrix::kMPersp2)); + } else if (inverse.getSkewX() != 0.0f || inverse.getSkewY() != 0.0f) { + matrixProc->Initialize>( + next, + SkVector{inverse.getTranslateX(), inverse.getTranslateY()}, + SkVector{inverse.getScaleX(), inverse.getScaleY()}, + SkVector{inverse.getSkewX(), inverse.getSkewY()}); + } else if (inverse.getScaleX() != 1.0f || inverse.getScaleY() != 1.0f) { + matrixProc->Initialize>( + next, + SkVector{inverse.getTranslateX(), inverse.getTranslateY()}, + SkVector{inverse.getScaleX(), inverse.getScaleY()}); + } else if (inverse.getTranslateX() != 0.0f || inverse.getTranslateY() != 0.0f) { + matrixProc->Initialize>( + next, + SkVector{inverse.getTranslateX(), inverse.getTranslateY()}); + } else { + return next; + } + return matrixProc->get(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Tile Stage + +template +class NearestTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface { +public: + template + NearestTileStage(Next* next, SkISize dimensions) + : fNext{next} + , fXStrategy{dimensions.width()} + , fYStrategy{dimensions.height()}{ } + + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { + fXStrategy.tileXPoints(&xs); + fYStrategy.tileYPoints(&ys); + fNext->pointListFew(n, xs, ys); + } + + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { + fXStrategy.tileXPoints(&xs); + fYStrategy.tileYPoints(&ys); + fNext->pointList4(xs, ys); + } + + // The span you pass must not be empty. + void pointSpan(Span span) override { + SkASSERT(!span.isEmpty()); + SkPoint start; SkScalar length; int count; + std::tie(start, length, count) = span; + SkScalar x = X(start); + SkScalar y = fYStrategy.tileY(Y(start)); + Span yAdjustedSpan{{x, y}, length, count}; + if (!fXStrategy.maybeProcessSpan(yAdjustedSpan, fNext)) { + span_fallback(span, this); + } + } + +private: + Next* const fNext; + XStrategy fXStrategy; + YStrategy fYStrategy; +}; + +template +class BilerpTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface { +public: + template + BilerpTileStage(Next* next, SkISize dimensions) + : fXMax(dimensions.width()) + , fYMax(dimensions.height()) + , fNext{next} + , fXStrategy{dimensions.width()} + , fYStrategy{dimensions.height()}{ } + + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { + fXStrategy.tileXPoints(&xs); + fYStrategy.tileYPoints(&ys); + // TODO: check to see if xs and ys are in range then just call pointListFew on next. + if (n >= 1) this->bilerpPoint(xs[0], ys[0]); + if (n >= 2) this->bilerpPoint(xs[1], ys[1]); + if (n >= 3) this->bilerpPoint(xs[2], ys[2]); + } + + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { + fXStrategy.tileXPoints(&xs); + fYStrategy.tileYPoints(&ys); + // TODO: check to see if xs and ys are in range then just call pointList4 on next. + this->bilerpPoint(xs[0], ys[0]); + this->bilerpPoint(xs[1], ys[1]); + this->bilerpPoint(xs[2], ys[2]); + this->bilerpPoint(xs[3], ys[3]); + } + + struct Wrapper { + void pointSpan(Span span) { + processor->breakIntoEdges(span); + } + + void repeatSpan(Span span, int32_t repeatCount) { + while (repeatCount --> 0) { + processor->pointSpan(span); + } + } + + BilerpTileStage* processor; + }; + + // The span you pass must not be empty. + void pointSpan(Span span) override { + SkASSERT(!span.isEmpty()); + + Wrapper wrapper = {this}; + if (!fXStrategy.maybeProcessSpan(span, &wrapper)) { + span_fallback(span, this); + } + } + +private: + void bilerpPoint(SkScalar x, SkScalar y) { + Sk4f txs = Sk4f{x} + Sk4f{-0.5f, 0.5f, -0.5f, 0.5f}; + Sk4f tys = Sk4f{y} + Sk4f{-0.5f, -0.5f, 0.5f, 0.5f}; + fXStrategy.tileXPoints(&txs); + fYStrategy.tileYPoints(&tys); + fNext->bilerpEdge(txs, tys); + } + + void handleEdges(Span span, SkScalar dx) { + SkPoint start; SkScalar length; int count; + std::tie(start, length, count) = span; + SkScalar x = X(start); + SkScalar y = Y(start); + SkScalar tiledY = fYStrategy.tileY(y); + while (count > 0) { + this->bilerpPoint(x, tiledY); + x += dx; + count -= 1; + } + } + + void yProcessSpan(Span span) { + SkScalar tiledY = fYStrategy.tileY(span.startY()); + if (0.5f <= tiledY && tiledY < fYMax - 0.5f ) { + Span tiledSpan{{span.startX(), tiledY}, span.length(), span.count()}; + fNext->pointSpan(tiledSpan); + } else { + // Convert to the Y0 bilerp sample set by shifting by -0.5f. Then tile that new y + // value and shift it back resulting in the working Y0. Do the same thing with Y1 but + // in the opposite direction. + SkScalar y0 = fYStrategy.tileY(span.startY() - 0.5f) + 0.5f; + SkScalar y1 = fYStrategy.tileY(span.startY() + 0.5f) - 0.5f; + Span newSpan{{span.startX(), y0}, span.length(), span.count()}; + fNext->bilerpSpan(newSpan, y1); + } + } + void breakIntoEdges(Span span) { + if (span.length() == 0) { + yProcessSpan(span); + } else { + SkScalar dx = span.length() / (span.count() - 1); + if (span.length() > 0) { + Span leftBorder = span.breakAt(0.5f, dx); + if (!leftBorder.isEmpty()) { + this->handleEdges(leftBorder, dx); + } + Span center = span.breakAt(fXMax - 0.5f, dx); + if (!center.isEmpty()) { + this->yProcessSpan(center); + } + + if (!span.isEmpty()) { + this->handleEdges(span, dx); + } + } else { + Span center = span.breakAt(fXMax + 0.5f, dx); + if (!span.isEmpty()) { + this->handleEdges(span, dx); + } + Span leftEdge = center.breakAt(0.5f, dx); + if (!center.isEmpty()) { + this->yProcessSpan(center); + } + if (!leftEdge.isEmpty()) { + this->handleEdges(leftEdge, dx); + } + + } + } + } + + SkScalar fXMax; + SkScalar fYMax; + Next* const fNext; + XStrategy fXStrategy; + YStrategy fYStrategy; +}; + +template +void make_tile_stage( + SkFilterQuality filterQuality, SkISize dimensions, + Next* next, SkLinearBitmapPipeline::TileStage* tileStage) { + if (filterQuality == kNone_SkFilterQuality) { + tileStage->Initialize>(next, dimensions); + } else { + tileStage->Initialize>(next, dimensions); + } +} +template +void choose_tiler_ymode( + SkShader::TileMode yMode, SkFilterQuality filterQuality, SkISize dimensions, + SkLinearBitmapPipeline::SampleProcessorInterface* next, + SkLinearBitmapPipeline::TileStage* tileStage) { + switch (yMode) { + case SkShader::kClamp_TileMode: + make_tile_stage(filterQuality, dimensions, next, tileStage); + break; + case SkShader::kRepeat_TileMode: + make_tile_stage(filterQuality, dimensions, next, tileStage); + break; + case SkShader::kMirror_TileMode: + make_tile_stage(filterQuality, dimensions, next, tileStage); + break; + } +}; + +static SkLinearBitmapPipeline::PointProcessorInterface* choose_tiler( + SkLinearBitmapPipeline::SampleProcessorInterface* next, + SkISize dimensions, + SkShader::TileMode xMode, + SkShader::TileMode yMode, + SkFilterQuality filterQuality, + SkScalar dx, + SkLinearBitmapPipeline::TileStage* tileStage) +{ + switch (xMode) { + case SkShader::kClamp_TileMode: + choose_tiler_ymode(yMode, filterQuality, dimensions, next, tileStage); + break; + case SkShader::kRepeat_TileMode: + if (dx == 1.0f && filterQuality == kNone_SkFilterQuality) { + choose_tiler_ymode( + yMode, kNone_SkFilterQuality, dimensions, next, tileStage); + } else { + choose_tiler_ymode( + yMode, filterQuality, dimensions, next, tileStage); + } + break; + case SkShader::kMirror_TileMode: + choose_tiler_ymode(yMode, filterQuality, dimensions, next, tileStage); + break; + } + + return tileStage->get(); +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Source Sampling Stage +template +class NearestNeighborSampler final : public SkLinearBitmapPipeline::SampleProcessorInterface { +public: + template + NearestNeighborSampler(Next* next, Args&&... args) + : fSampler{next, std::forward(args)...} { } + + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { + fSampler.nearestListFew(n, xs, ys); + } + + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { + fSampler.nearestList4(xs, ys); + } + + void pointSpan(Span span) override { + fSampler.nearestSpan(span); + } + + void repeatSpan(Span span, int32_t repeatCount) override { + while (repeatCount > 0) { + fSampler.nearestSpan(span); + repeatCount--; + } + } + + void VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) override { + SkFAIL("Using nearest neighbor sampler, but calling a bilerpEdge."); + } + + void bilerpSpan(Span span, SkScalar y) override { + SkFAIL("Using nearest neighbor sampler, but calling a bilerpSpan."); + } + +private: + GeneralSampler fSampler; +}; + +template +class BilerpSampler final : public SkLinearBitmapPipeline::SampleProcessorInterface { +public: + template + BilerpSampler(Next* next, Args&&... args) + : fSampler{next, std::forward(args)...} { } + + void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { + fSampler.bilerpListFew(n, xs, ys); + } + + void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override { + fSampler.bilerpList4(xs, ys); + } + + void pointSpan(Span span) override { + fSampler.bilerpSpan(span); + } + + void repeatSpan(Span span, int32_t repeatCount) override { + while (repeatCount > 0) { + fSampler.bilerpSpan(span); + repeatCount--; + } + } + + void VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) override { + fSampler.bilerpEdge(xs, ys); + } + + void bilerpSpan(Span span, SkScalar y) override { + fSampler.bilerpSpanWithY(span, y); + } + +private: + GeneralSampler fSampler; +}; + +using Placer = SkLinearBitmapPipeline::PixelPlacerInterface; + +template