summaryrefslogtreecommitdiff
path: root/chromium/skia
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/skia
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/skia')
-rw-r--r--chromium/skia/OWNERS21
-rw-r--r--chromium/skia/README.chromium24
-rw-r--r--chromium/skia/config/SkUserConfig.h244
-rw-r--r--chromium/skia/ext/SkMemory_new_handler.cpp77
-rw-r--r--chromium/skia/ext/SkThread_chrome.cc88
-rw-r--r--chromium/skia/ext/SkTypeface_fake.cpp21
-rw-r--r--chromium/skia/ext/analysis_canvas.cc416
-rw-r--r--chromium/skia/ext/analysis_canvas.h164
-rw-r--r--chromium/skia/ext/analysis_canvas_unittest.cc406
-rw-r--r--chromium/skia/ext/benchmarking_canvas.cc238
-rw-r--r--chromium/skia/ext/benchmarking_canvas.h50
-rw-r--r--chromium/skia/ext/bitmap_platform_device.h36
-rw-r--r--chromium/skia/ext/bitmap_platform_device_android.cc99
-rw-r--r--chromium/skia/ext/bitmap_platform_device_android.h60
-rw-r--r--chromium/skia/ext/bitmap_platform_device_data.h102
-rw-r--r--chromium/skia/ext/bitmap_platform_device_linux.cc219
-rw-r--r--chromium/skia/ext/bitmap_platform_device_linux.h114
-rw-r--r--chromium/skia/ext/bitmap_platform_device_mac.cc303
-rw-r--r--chromium/skia/ext/bitmap_platform_device_mac.h88
-rw-r--r--chromium/skia/ext/bitmap_platform_device_mac_unittest.cc69
-rw-r--r--chromium/skia/ext/bitmap_platform_device_win.cc356
-rw-r--r--chromium/skia/ext/bitmap_platform_device_win.h99
-rw-r--r--chromium/skia/ext/convolver.cc716
-rw-r--r--chromium/skia/ext/convolver.h235
-rw-r--r--chromium/skia/ext/convolver_SSE2.cc457
-rw-r--r--chromium/skia/ext/convolver_SSE2.h27
-rw-r--r--chromium/skia/ext/convolver_mips_dspr2.cc478
-rw-r--r--chromium/skia/ext/convolver_mips_dspr2.h25
-rw-r--r--chromium/skia/ext/convolver_unittest.cc535
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/00_pc_clean.pngbin0 -> 289 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/00_vc_clean.pngbin0 -> 289 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/01_pc_drawargb.pngbin0 -> 289 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/01_vc_drawargb.pngbin0 -> 289 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.pngbin0 -> 537 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.pngbin0 -> 537 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.pngbin0 -> 417 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.pngbin0 -> 417 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.pngbin0 -> 417 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.pngbin0 -> 417 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.pngbin0 -> 433 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.pngbin0 -> 433 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.pngbin0 -> 109 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.pngbin0 -> 109 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.pngbin0 -> 126 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.pngbin0 -> 126 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.pngbin0 -> 271 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.pngbin0 -> 271 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/bitmaps/00_pc_opaque.pngbin0 -> 5140 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/bitmaps/00_vc_opaque.pngbin0 -> 5140 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/bitmaps/01_pc_alpha.pngbin0 -> 2699 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/bitmaps/01_vc_alpha.pngbin0 -> 2699 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/bitmaps/bitmap_alpha.pngbin0 -> 422 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/bitmaps/bitmap_opaque.pngbin0 -> 3287 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/00_pc_circle_stroke.pngbin0 -> 383 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/00_vc_circle_stroke.pngbin0 -> 410 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/01_pc_circle_fill.pngbin0 -> 463 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/01_vc_circle_fill.pngbin0 -> 481 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/02_pc_circle_over_strike.pngbin0 -> 477 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/02_vc_circle_over_strike.pngbin0 -> 513 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.pngbin0 -> 566 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.pngbin0 -> 602 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/04_pc_mixed_stroke.pngbin0 -> 1180 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/circles/04_vc_mixed_stroke.pngbin0 -> 1228 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingclean/00_pc_clipped.pngbin0 -> 1354 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingclean/00_vc_clipped.pngbin0 -> 1354 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingclean/01_pc_unclipped.pngbin0 -> 4683 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingclean/01_vc_unclipped.pngbin0 -> 4683 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingcombined/00_pc_combined.pngbin0 -> 1354 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingcombined/00_vc_combined.pngbin0 -> 1354 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingintersect/00_pc_intersect.pngbin0 -> 1211 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingintersect/00_vc_intersect.pngbin0 -> 1211 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingpath/00_pc_path.pngbin0 -> 1132 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingpath/00_vc_path.pngbin0 -> 1132 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingrect/00_pc_rect.pngbin0 -> 1459 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/clippingrect/00_vc_rect.pngbin0 -> 1459 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/diagonallines/00_pc_nw-se.pngbin0 -> 536 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/diagonallines/00_vc_nw-se.pngbin0 -> 536 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/diagonallines/01_pc_sw-ne.pngbin0 -> 735 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/diagonallines/01_vc_sw-ne.pngbin0 -> 737 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/diagonallines/02_pc_ne-sw.pngbin0 -> 756 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/diagonallines/02_vc_ne-sw.pngbin0 -> 760 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/diagonallines/03_pc_se-nw.pngbin0 -> 765 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/diagonallines/03_vc_se-nw.pngbin0 -> 781 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/lineorientation/00_pc_horizontal.pngbin0 -> 313 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/lineorientation/00_vc_horizontal.pngbin0 -> 319 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/lineorientation/01_pc_vertical.pngbin0 -> 328 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/lineorientation/01_vc_vertical.pngbin0 -> 344 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.pngbin0 -> 333 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.pngbin0 -> 348 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/lineorientation/03_pc_vertical_180.pngbin0 -> 332 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/lineorientation/03_vc_vertical_180.pngbin0 -> 351 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/matrix/00_pc_translate1.pngbin0 -> 5139 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/matrix/00_vc_translate1.pngbin0 -> 5139 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/matrix/01_pc_translate2.pngbin0 -> 4645 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/matrix/01_vc_translate2.pngbin0 -> 4645 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/matrix/02_pc_scale.pngbin0 -> 6566 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/matrix/02_vc_scale.pngbin0 -> 12292 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/matrix/03_pc_rotate.pngbin0 -> 9749 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/matrix/03_vc_rotate.pngbin0 -> 13795 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/patheffects/00_pc_dash_line.pngbin0 -> 299 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/patheffects/00_vc_dash_line.pngbin0 -> 299 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/patheffects/01_pc_dash_path.pngbin0 -> 348 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/patheffects/01_vc_dash_path.pngbin0 -> 343 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/patheffects/02_pc_dash_rect.pngbin0 -> 387 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/patheffects/02_vc_dash_rect.pngbin0 -> 395 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/patheffects/03_pc_circle.pngbin0 -> 497 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/patheffects/03_vc_circle.pngbin0 -> 519 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.pngbin0 -> 307 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.pngbin0 -> 307 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.pngbin0 -> 313 bytes
-rw-r--r--chromium/skia/ext/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.pngbin0 -> 319 bytes
-rw-r--r--chromium/skia/ext/google_logging.cc25
-rw-r--r--chromium/skia/ext/image_operations.cc544
-rw-r--r--chromium/skia/ext/image_operations.h133
-rw-r--r--chromium/skia/ext/image_operations_bench.cc293
-rw-r--r--chromium/skia/ext/image_operations_unittest.cc731
-rw-r--r--chromium/skia/ext/lazy_pixel_ref.cc15
-rw-r--r--chromium/skia/ext/lazy_pixel_ref.h39
-rw-r--r--chromium/skia/ext/lazy_pixel_ref_utils.cc409
-rw-r--r--chromium/skia/ext/lazy_pixel_ref_utils.h33
-rw-r--r--chromium/skia/ext/lazy_pixel_ref_utils_unittest.cc735
-rw-r--r--chromium/skia/ext/paint_simplifier.cc41
-rw-r--r--chromium/skia/ext/paint_simplifier.h33
-rw-r--r--chromium/skia/ext/platform_canvas.cc78
-rw-r--r--chromium/skia/ext/platform_canvas.h188
-rw-r--r--chromium/skia/ext/platform_canvas_unittest.cc459
-rw-r--r--chromium/skia/ext/platform_device.cc80
-rw-r--r--chromium/skia/ext/platform_device.h173
-rw-r--r--chromium/skia/ext/platform_device_linux.cc17
-rw-r--r--chromium/skia/ext/platform_device_mac.cc155
-rw-r--r--chromium/skia/ext/platform_device_win.cc237
-rw-r--r--chromium/skia/ext/recursive_gaussian_convolution.cc270
-rw-r--r--chromium/skia/ext/recursive_gaussian_convolution.h71
-rw-r--r--chromium/skia/ext/recursive_gaussian_convolution_unittest.cc395
-rw-r--r--chromium/skia/ext/refptr.h118
-rw-r--r--chromium/skia/ext/refptr_unittest.cc125
-rw-r--r--chromium/skia/ext/skia_trace_shim.h17
-rw-r--r--chromium/skia/ext/skia_utils_base.cc53
-rw-r--r--chromium/skia/ext/skia_utils_base.h33
-rw-r--r--chromium/skia/ext/skia_utils_ios.h40
-rw-r--r--chromium/skia/ext/skia_utils_ios.mm103
-rw-r--r--chromium/skia/ext/skia_utils_ios_unittest.mm157
-rw-r--r--chromium/skia/ext/skia_utils_mac.h122
-rw-r--r--chromium/skia/ext/skia_utils_mac.mm429
-rw-r--r--chromium/skia/ext/skia_utils_mac_unittest.mm246
-rw-r--r--chromium/skia/ext/skia_utils_win.cc66
-rw-r--r--chromium/skia/ext/skia_utils_win.h49
-rw-r--r--chromium/skia/ext/vector_canvas.cc38
-rw-r--r--chromium/skia/ext/vector_canvas.h40
-rw-r--r--chromium/skia/ext/vector_canvas_unittest.cc970
-rw-r--r--chromium/skia/ext/vector_platform_device_emf_win.cc986
-rw-r--r--chromium/skia/ext/vector_platform_device_emf_win.h140
-rw-r--r--chromium/skia/ext/vector_platform_device_skia.cc88
-rw-r--r--chromium/skia/ext/vector_platform_device_skia.h60
-rw-r--r--chromium/skia/fix_for_1186198.diff38
-rw-r--r--chromium/skia/skia.gyp139
-rw-r--r--chromium/skia/skia_Prefix.pch12
-rw-r--r--chromium/skia/skia_chrome.gypi132
-rw-r--r--chromium/skia/skia_common.gypi33
-rw-r--r--chromium/skia/skia_library.gypi472
-rw-r--r--chromium/skia/skia_library_opts.gyp242
-rw-r--r--chromium/skia/skia_system.gypi24
-rw-r--r--chromium/skia/skia_test_expectations.txt70
-rw-r--r--chromium/skia/tile_patch.diff284
164 files changed, 16247 insertions, 0 deletions
diff --git a/chromium/skia/OWNERS b/chromium/skia/OWNERS
new file mode 100644
index 00000000000..fe892fd24cf
--- /dev/null
+++ b/chromium/skia/OWNERS
@@ -0,0 +1,21 @@
+alokp@chromium.org
+borenet@google.com
+bsalomon@google.com
+bungeman@google.com
+djsollen@google.com
+edisonn@google.com
+epoger@google.com
+fmalita@google.com
+junov@chromium.org
+jvanverth@google.com
+reed@google.com
+rmistry@google.com
+robertphillips@google.com
+scroggo@google.com
+senorblanco@chromium.org
+sugoi@google.com
+thakis@chromium.org
+tomhudson@google.com
+vandebo@chromium.org
+
+
diff --git a/chromium/skia/README.chromium b/chromium/skia/README.chromium
new file mode 100644
index 00000000000..76a92840d71
--- /dev/null
+++ b/chromium/skia/README.chromium
@@ -0,0 +1,24 @@
+This is a copy of the Skia source tree. In the original repository, the include
+directories and the "corecg" directories are separated out. On top of
+ libs/graphics -> skia
+we have the following mappings from source repository to our tree:
+ include/corecg -> skia/include/corecg
+ include/graphics -> skia/include
+ libs/corecg -> skia/corecg
+
+platform/* are our own files that provide extra functionality we need our
+Skia to implement.
+
+DO NOT CHANGE THE SKIA FILES IN OUR TREE. These will be overwritten when we
+sync to newer versions of Skia. The exception is platform/
+
+THE EXCEPTION IS include/corecg/SkUserConfig.h which are the application's
+definition of its options and environment. This file must be manually merged
+with any changes in the Skia tree so that our options are preserved and we
+also pick up any important changes they make.
+
+ -- brettw@google.com, 28 December 2006
+
+Patches we are tracking locally (until Skia is fixed upstream):
+fix_for_1186198.diff -- eseidel, 6/4/08, BUG=1186198
+linux_patch.diff
diff --git a/chromium/skia/config/SkUserConfig.h b/chromium/skia/config/SkUserConfig.h
new file mode 100644
index 00000000000..a0cb64f941c
--- /dev/null
+++ b/chromium/skia/config/SkUserConfig.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SkUserConfig_DEFINED
+#define SkUserConfig_DEFINED
+
+/* SkTypes.h, the root of the public header files, does the following trick:
+
+ #include <SkPreConfig.h>
+ #include <SkUserConfig.h>
+ #include <SkPostConfig.h>
+
+ SkPreConfig.h runs first, and it is responsible for initializing certain
+ skia defines.
+
+ SkPostConfig.h runs last, and its job is to just check that the final
+ defines are consistent (i.e. that we don't have mutually conflicting
+ defines).
+
+ SkUserConfig.h (this file) runs in the middle. It gets to change or augment
+ the list of flags initially set in preconfig, and then postconfig checks
+ that everything still makes sense.
+
+ Below are optional defines that add, subtract, or change default behavior
+ in Skia. Your port can locally edit this file to enable/disable flags as
+ you choose, or these can be delared on your command line (i.e. -Dfoo).
+
+ By default, this include file will always default to having all of the flags
+ commented out, so including it will have no effect.
+*/
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* Scalars (the fractional value type in skia) can be implemented either as
+ floats or 16.16 integers (fixed). Exactly one of these two symbols must be
+ defined.
+*/
+//#define SK_SCALAR_IS_FLOAT
+//#define SK_SCALAR_IS_FIXED
+
+
+/* Somewhat independent of how SkScalar is implemented, Skia also wants to know
+ if it can use floats at all. Naturally, if SK_SCALAR_IS_FLOAT is defined,
+ then so muse SK_CAN_USE_FLOAT, but if scalars are fixed, SK_CAN_USE_FLOAT
+ can go either way.
+ */
+//#define SK_CAN_USE_FLOAT
+
+/* For some performance-critical scalar operations, skia will optionally work
+ around the standard float operators if it knows that the CPU does not have
+ native support for floats. If your environment uses software floating point,
+ define this flag.
+ */
+//#define SK_SOFTWARE_FLOAT
+
+
+/* Skia has lots of debug-only code. Often this is just null checks or other
+ parameter checking, but sometimes it can be quite intrusive (e.g. check that
+ each 32bit pixel is in premultiplied form). This code can be very useful
+ during development, but will slow things down in a shipping product.
+
+ By default, these mutually exclusive flags are defined in SkPreConfig.h,
+ based on the presence or absence of NDEBUG, but that decision can be changed
+ here.
+ */
+//#define SK_DEBUG
+//#define SK_RELEASE
+
+
+/* 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.
+ */
+//#define SK_CPU_BENDIAN
+//#define SK_CPU_LENDIAN
+
+
+/* Some compilers don't support long long for 64bit integers. If yours does
+ not, define this to the appropriate type.
+ */
+//#define SkLONGLONG int64_t
+
+
+/* Some envorinments do not suport writable globals (eek!). If yours does not,
+ define this flag.
+ */
+//#define SK_USE_RUNTIME_GLOBALS
+
+/* If zlib is available and you want to support the flate compression
+ algorithm (used in PDF generation), define SK_ZLIB_INCLUDE to be the
+ include path.
+ */
+//#define SK_ZLIB_INCLUDE <zlib.h>
+#define SK_ZLIB_INCLUDE "third_party/zlib/zlib.h"
+
+/* 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 for font subsetting when generating
+ PDF documents.
+ */
+#define SK_SFNTLY_SUBSETTER \
+ "third_party/sfntly/cpp/src/sample/chromium/font_subsetter.h"
+
+/* Define this to remove dimension checks on bitmaps. Not all blits will be
+ correct yet, so this is mostly for debugging the implementation.
+ */
+//#define SK_ALLOW_OVER_32K_BITMAPS
+
+
+/* To write debug messages to a console, skia will call SkDebugf(...) following
+ printf conventions (e.g. const char* format, ...). If you want to redirect
+ this to something other than printf, define yours here
+ */
+//#define SkDebugf(...) MyFunction(__VA_ARGS__)
+
+
+/* If SK_DEBUG is defined, then you can optionally define SK_SUPPORT_UNITTEST
+ which will run additional self-tests at startup. These can take a long time,
+ so this flag is optional.
+ */
+#ifdef SK_DEBUG
+#define SK_SUPPORT_UNITTEST
+#endif
+
+/* If your system embeds skia and has complex event logging, define this
+ symbol to name a file that maps the following macros to your system's
+ equivalents:
+ SK_TRACE_EVENT0(event)
+ SK_TRACE_EVENT1(event, name1, value1)
+ SK_TRACE_EVENT2(event, name1, value1, name2, value2)
+ src/utils/SkDebugTrace.h has a trivial implementation that writes to
+ the debug output stream. If SK_USER_TRACE_INCLUDE_FILE is not defined,
+ SkTrace.h will define the above three macros to do nothing.
+*/
+#undef SK_USER_TRACE_INCLUDE_FILE
+
+// ===== Begin Chrome-specific definitions =====
+
+#define SK_SCALAR_IS_FLOAT
+#undef SK_SCALAR_IS_FIXED
+
+#define SK_MSCALAR_IS_DOUBLE
+#undef SK_MSCALAR_IS_FLOAT
+
+#define GR_MAX_OFFSCREEN_AA_DIM 512
+
+// Log the file and line number for assertions.
+#define SkDebugf(...) SkDebugf_FileLine(__FILE__, __LINE__, false, __VA_ARGS__)
+SK_API void SkDebugf_FileLine(const char* file, int line, bool fatal,
+ const char* format, ...);
+
+// Marking the debug print as "fatal" will cause a debug break, so we don't need
+// a separate crash call here.
+#define SK_DEBUGBREAK(cond) do { if (!(cond)) { \
+ SkDebugf_FileLine(__FILE__, __LINE__, true, \
+ "%s:%d: failed assertion \"%s\"\n", \
+ __FILE__, __LINE__, #cond); } } while (false)
+
+#if !defined(ANDROID) // On Android, we use the skia default settings.
+#define SK_A32_SHIFT 24
+#define SK_R32_SHIFT 16
+#define SK_G32_SHIFT 8
+#define SK_B32_SHIFT 0
+#endif
+
+#if defined(SK_BUILD_FOR_WIN32)
+
+#define SK_BUILD_FOR_WIN
+
+// VC8 doesn't support stdint.h, so we define those types here.
+#define SK_IGNORE_STDINT_DOT_H
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef short int16_t;
+typedef unsigned short uint16_t;
+typedef int int32_t;
+typedef unsigned uint32_t;
+
+// VC doesn't support __restrict__, so make it a NOP.
+#undef SK_RESTRICT
+#define SK_RESTRICT
+
+// Skia uses this deprecated bzero function to fill zeros into a string.
+#define bzero(str, len) memset(str, 0, len)
+
+#elif defined(SK_BUILD_FOR_MAC)
+
+#define SK_CPU_LENDIAN
+#undef SK_CPU_BENDIAN
+
+#elif defined(SK_BUILD_FOR_UNIX)
+
+// Prefer FreeType's emboldening algorithm to Skia's
+// TODO: skia used to just use hairline, but has improved since then, so
+// we should revisit this choice...
+#define SK_USE_FREETYPE_EMBOLDEN
+
+#ifdef SK_CPU_BENDIAN
+// Above we set the order for ARGB channels in registers. I suspect that, on
+// big endian machines, you can keep this the same and everything will work.
+// The in-memory order will be different, of course, but as long as everything
+// is reading memory as words rather than bytes, it will all work. However, if
+// you find that colours are messed up I thought that I would leave a helpful
+// locator for you. Also see the comments in
+// base/gfx/bitmap_platform_device_linux.h
+#error Read the comment at this location
+#endif
+
+#endif
+
+// The default crash macro writes to badbeef which can cause some strange
+// problems. Instead, pipe this through to the logging function as a fatal
+// assertion.
+#define SK_CRASH() SkDebugf_FileLine(__FILE__, __LINE__, true, "SK_CRASH")
+
+// Uncomment the following line to forward skia trace events to Chrome
+// tracing.
+// #define SK_USER_TRACE_INCLUDE_FILE "skia/ext/skia_trace_shim.h"
+
+// ===== End Chrome-specific definitions =====
+
+#endif
diff --git a/chromium/skia/ext/SkMemory_new_handler.cpp b/chromium/skia/ext/SkMemory_new_handler.cpp
new file mode 100644
index 00000000000..dbbc4944512
--- /dev/null
+++ b/chromium/skia/ext/SkMemory_new_handler.cpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <new>
+
+#include "base/process/memory.h"
+
+#include "third_party/skia/include/core/SkTypes.h"
+#include "third_party/skia/include/core/SkThread.h"
+
+// This implementation of sk_malloc_flags() and friends is identical
+// to SkMemory_malloc.c, except that it disables the CRT's new_handler
+// during malloc(), when SK_MALLOC_THROW is not set (ie., when
+// sk_malloc_flags() would not abort on NULL).
+
+SK_DECLARE_STATIC_MUTEX(gSkNewHandlerMutex);
+
+void sk_throw() {
+ SkASSERT(!"sk_throw");
+ abort();
+}
+
+void sk_out_of_memory(void) {
+ SkASSERT(!"sk_out_of_memory");
+ abort();
+}
+
+void* sk_malloc_throw(size_t size) {
+ return sk_malloc_flags(size, SK_MALLOC_THROW);
+}
+
+void* sk_realloc_throw(void* addr, size_t size) {
+ void* p = realloc(addr, size);
+ if (size == 0) {
+ return p;
+ }
+ if (p == NULL) {
+ sk_throw();
+ }
+ return p;
+}
+
+void sk_free(void* p) {
+ if (p) {
+ free(p);
+ }
+}
+
+void* sk_malloc_flags(size_t size, unsigned flags) {
+ void* p;
+#if defined(ANDROID)
+ // Android doesn't have std::set_new_handler.
+ p = malloc(size);
+#else
+ if (!(flags & SK_MALLOC_THROW)) {
+#if defined(OS_MACOSX) && !defined(OS_IOS)
+ p = base::UncheckedMalloc(size);
+#else
+ SkAutoMutexAcquire lock(gSkNewHandlerMutex);
+ std::new_handler old_handler = std::set_new_handler(NULL);
+ p = malloc(size);
+ std::set_new_handler(old_handler);
+#endif
+ } else {
+ p = malloc(size);
+ }
+#endif
+ if (p == NULL) {
+ if (flags & SK_MALLOC_THROW) {
+ sk_throw();
+ }
+ }
+ return p;
+}
diff --git a/chromium/skia/ext/SkThread_chrome.cc b/chromium/skia/ext/SkThread_chrome.cc
new file mode 100644
index 00000000000..f379bebee71
--- /dev/null
+++ b/chromium/skia/ext/SkThread_chrome.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "third_party/skia/include/core/SkThread.h"
+
+#include <new>
+
+#include "base/atomicops.h"
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/synchronization/lock.h"
+
+/** Adds one to the int specified by the address (in a thread-safe manner), and
+ returns the previous value.
+ No additional memory barrier is required.
+ This must act as a compiler barrier.
+*/
+int32_t sk_atomic_inc(int32_t* addr) {
+ // sk_atomic_inc is expected to return the old value,
+ // Barrier_AtomicIncrement returns the new value.
+ return base::subtle::NoBarrier_AtomicIncrement(addr, 1) - 1;
+}
+
+/* Subtracts one from the int specified by the address (in a thread-safe
+ manner), and returns the previous value.
+ Expected to act as a release (SL/S) memory barrier and a compiler barrier.
+*/
+int32_t sk_atomic_dec(int32_t* addr) {
+ // sk_atomic_dec is expected to return the old value,
+ // Barrier_AtomicIncrement returns the new value.
+ return base::subtle::Barrier_AtomicIncrement(addr, -1) + 1;
+}
+/** If sk_atomic_dec does not act as an aquire (L/SL) barrier, this is expected
+ to act as an aquire (L/SL) memory barrier and as a compiler barrier.
+*/
+void sk_membar_aquire__after_atomic_dec() { }
+
+/** Adds one to the int specified by the address iff the int specified by the
+ address is not zero (in a thread-safe manner), and returns the previous
+ value.
+ No additional memory barrier is required.
+ This must act as a compiler barrier.
+*/
+int32_t sk_atomic_conditional_inc(int32_t* addr) {
+ int32_t value = *addr;
+
+ while (true) {
+ if (value == 0) {
+ return 0;
+ }
+
+ int32_t before;
+ before = base::subtle::Acquire_CompareAndSwap(addr, value, value + 1);
+
+ if (before == value) {
+ return value;
+ } else {
+ value = before;
+ }
+ }
+}
+/** If sk_atomic_conditional_inc does not act as an aquire (L/SL) barrier, this
+ is expected to act as an aquire (L/SL) memory barrier and as a compiler
+ barrier.
+*/
+void sk_membar_aquire__after_atomic_conditional_inc() { }
+
+SkMutex::SkMutex() {
+ COMPILE_ASSERT(sizeof(base::Lock) <= sizeof(fStorage), Lock_is_too_big_for_SkMutex);
+ base::Lock* lock = reinterpret_cast<base::Lock*>(fStorage);
+ new(lock) base::Lock();
+}
+
+SkMutex::~SkMutex() {
+ base::Lock* lock = reinterpret_cast<base::Lock*>(fStorage);
+ lock->~Lock();
+}
+
+void SkMutex::acquire() {
+ base::Lock* lock = reinterpret_cast<base::Lock*>(fStorage);
+ lock->Acquire();
+}
+
+void SkMutex::release() {
+ base::Lock* lock = reinterpret_cast<base::Lock*>(fStorage);
+ lock->Release();
+}
diff --git a/chromium/skia/ext/SkTypeface_fake.cpp b/chromium/skia/ext/SkTypeface_fake.cpp
new file mode 100644
index 00000000000..dea36a1ff45
--- /dev/null
+++ b/chromium/skia/ext/SkTypeface_fake.cpp
@@ -0,0 +1,21 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "SkTypeface.h"
+
+// ===== Begin Chrome-specific definitions =====
+
+uint32_t SkTypeface::UniqueID(const SkTypeface* face)
+{
+ return 0;
+}
+
+void SkTypeface::serialize(SkWStream* stream) const {
+}
+
+SkTypeface* SkTypeface::Deserialize(SkStream* stream) {
+ return NULL;
+}
+
+// ===== End Chrome-specific definitions =====
diff --git a/chromium/skia/ext/analysis_canvas.cc b/chromium/skia/ext/analysis_canvas.cc
new file mode 100644
index 00000000000..a2d397c89db
--- /dev/null
+++ b/chromium/skia/ext/analysis_canvas.cc
@@ -0,0 +1,416 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "skia/ext/analysis_canvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkDraw.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "third_party/skia/src/core/SkRasterClip.h"
+#include "ui/gfx/rect_conversions.h"
+
+namespace {
+
+const int kNoLayer = -1;
+
+bool IsSolidColorPaint(const SkPaint& paint) {
+ SkXfermode::Mode xfermode;
+
+ // getXfermode can return a NULL, but that is handled
+ // gracefully by AsMode (NULL turns into kSrcOver mode).
+ SkXfermode::AsMode(paint.getXfermode(), &xfermode);
+
+ // Paint is solid color if the following holds:
+ // - Alpha is 1.0, style is fill, and there are no special effects
+ // - Xfer mode is either kSrc or kSrcOver (kSrcOver is equivalent
+ // to kSrc if source alpha is 1.0, which is already checked).
+ return (paint.getAlpha() == 255 &&
+ !paint.getShader() &&
+ !paint.getLooper() &&
+ !paint.getMaskFilter() &&
+ !paint.getColorFilter() &&
+ paint.getStyle() == SkPaint::kFill_Style &&
+ (xfermode == SkXfermode::kSrc_Mode ||
+ xfermode == SkXfermode::kSrcOver_Mode));
+}
+
+bool IsFullQuad(const SkDraw& draw,
+ const SkRect& canvas_rect,
+ const SkRect& drawn_rect) {
+
+ // If the transform results in a non-axis aligned
+ // rect, then be conservative and return false.
+ if (!draw.fMatrix->rectStaysRect())
+ return false;
+
+ SkRect draw_bitmap_rect;
+ draw.fBitmap->getBounds(&draw_bitmap_rect);
+ SkRect clip_rect = SkRect::Make(draw.fRC->getBounds());
+ SkRect device_rect;
+ draw.fMatrix->mapRect(&device_rect, drawn_rect);
+
+ // The drawn rect covers the full canvas, if the following conditions hold:
+ // - Clip rect is an actual rectangle.
+ // - The rect we're drawing (post-transform) contains the clip rect.
+ // That is, all of clip rect will be colored by the rect.
+ // - Clip rect contains the canvas rect.
+ // That is, we're not clipping to a portion of this canvas.
+ // - The bitmap into which the draw call happens is at least as
+ // big as the canvas rect
+ return draw.fRC->isRect() &&
+ device_rect.contains(clip_rect) &&
+ clip_rect.contains(canvas_rect) &&
+ draw_bitmap_rect.contains(canvas_rect);
+}
+
+} // namespace
+
+namespace skia {
+
+AnalysisDevice::AnalysisDevice(const SkBitmap& bitmap)
+ : INHERITED(bitmap),
+ is_forced_not_solid_(false),
+ is_forced_not_transparent_(false),
+ is_solid_color_(true),
+ is_transparent_(true),
+ has_text_(false) {}
+
+AnalysisDevice::~AnalysisDevice() {}
+
+bool AnalysisDevice::GetColorIfSolid(SkColor* color) const {
+ if (is_transparent_) {
+ *color = SK_ColorTRANSPARENT;
+ return true;
+ }
+ if (is_solid_color_) {
+ *color = color_;
+ return true;
+ }
+ return false;
+}
+
+bool AnalysisDevice::HasText() const {
+ return has_text_;
+}
+
+void AnalysisDevice::SetForceNotSolid(bool flag) {
+ is_forced_not_solid_ = flag;
+ if (is_forced_not_solid_)
+ is_solid_color_ = false;
+}
+
+void AnalysisDevice::SetForceNotTransparent(bool flag) {
+ is_forced_not_transparent_ = flag;
+ if (is_forced_not_transparent_)
+ is_transparent_ = false;
+}
+
+void AnalysisDevice::clear(SkColor color) {
+ is_transparent_ = (!is_forced_not_transparent_ && SkColorGetA(color) == 0);
+ has_text_ = false;
+
+ if (!is_forced_not_solid_ && SkColorGetA(color) == 255) {
+ is_solid_color_ = true;
+ color_ = color;
+ } else {
+ is_solid_color_ = false;
+ }
+}
+
+void AnalysisDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+}
+
+void AnalysisDevice::drawPoints(const SkDraw& draw,
+ SkCanvas::PointMode mode,
+ size_t count,
+ const SkPoint points[],
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+}
+
+void AnalysisDevice::drawRect(const SkDraw& draw,
+ const SkRect& rect,
+ const SkPaint& paint) {
+ bool does_cover_canvas =
+ IsFullQuad(draw, SkRect::MakeWH(width(), height()), rect);
+
+ SkXfermode::Mode xfermode;
+ SkXfermode::AsMode(paint.getXfermode(), &xfermode);
+
+ // This canvas will become transparent if the following holds:
+ // - The quad is a full tile quad
+ // - We're not in "forced not transparent" mode
+ // - Transfer mode is clear (0 color, 0 alpha)
+ //
+ // If the paint alpha is not 0, or if the transfrer mode is
+ // not src, then this canvas will not be transparent.
+ //
+ // In all other cases, we keep the current transparent value
+ if (does_cover_canvas &&
+ !is_forced_not_transparent_ &&
+ xfermode == SkXfermode::kClear_Mode) {
+ is_transparent_ = true;
+ has_text_ = false;
+ } else if (paint.getAlpha() != 0 || xfermode != SkXfermode::kSrc_Mode) {
+ is_transparent_ = false;
+ }
+
+ // This bitmap is solid if and only if the following holds.
+ // Note that this might be overly conservative:
+ // - We're not in "forced not solid" mode
+ // - Paint is solid color
+ // - The quad is a full tile quad
+ if (!is_forced_not_solid_ && IsSolidColorPaint(paint) && does_cover_canvas) {
+ is_solid_color_ = true;
+ color_ = paint.getColor();
+ has_text_ = false;
+ } else {
+ is_solid_color_ = false;
+ }
+}
+
+void AnalysisDevice::drawOval(const SkDraw& draw,
+ const SkRect& oval,
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+}
+
+void AnalysisDevice::drawPath(const SkDraw& draw,
+ const SkPath& path,
+ const SkPaint& paint,
+ const SkMatrix* pre_path_matrix,
+ bool path_is_mutable) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+}
+
+void AnalysisDevice::drawBitmap(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkMatrix& matrix,
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+}
+
+void AnalysisDevice::drawSprite(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ int x,
+ int y,
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+}
+
+void AnalysisDevice::drawBitmapRect(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkRect* src_or_null,
+ const SkRect& dst,
+ const SkPaint& paint) {
+ // Call drawRect to determine transparency,
+ // but reset solid color to false.
+ drawRect(draw, dst, paint);
+ is_solid_color_ = false;
+}
+
+void AnalysisDevice::drawText(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+ has_text_ = true;
+}
+
+void AnalysisDevice::drawPosText(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkScalar pos[],
+ SkScalar const_y,
+ int scalars_per_pos,
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+ has_text_ = true;
+}
+
+void AnalysisDevice::drawTextOnPath(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+ has_text_ = true;
+}
+
+#ifdef SK_BUILD_FOR_ANDROID
+void AnalysisDevice::drawPosTextOnPath(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkPoint pos[],
+ const SkPaint& paint,
+ const SkPath& path,
+ const SkMatrix* matrix) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+ has_text_ = true;
+}
+#endif
+
+void AnalysisDevice::drawVertices(const SkDraw& draw,
+ SkCanvas::VertexMode,
+ int vertex_count,
+ const SkPoint verts[],
+ const SkPoint texs[],
+ const SkColor colors[],
+ SkXfermode* xmode,
+ const uint16_t indices[],
+ int index_count,
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+}
+
+void AnalysisDevice::drawDevice(const SkDraw& draw,
+ SkDevice* device,
+ int x,
+ int y,
+ const SkPaint& paint) {
+ is_solid_color_ = false;
+ is_transparent_ = false;
+}
+
+AnalysisCanvas::AnalysisCanvas(AnalysisDevice* device)
+ : INHERITED(device),
+ saved_stack_size_(0),
+ force_not_solid_stack_level_(kNoLayer),
+ force_not_transparent_stack_level_(kNoLayer) {}
+
+AnalysisCanvas::~AnalysisCanvas() {}
+
+bool AnalysisCanvas::GetColorIfSolid(SkColor* color) const {
+ return (static_cast<AnalysisDevice*>(getDevice()))->GetColorIfSolid(color);
+}
+
+bool AnalysisCanvas::HasText() const {
+ return (static_cast<AnalysisDevice*>(getDevice()))->HasText();
+}
+
+bool AnalysisCanvas::abortDrawing() {
+ // Early out as soon as we have detected that the tile has text.
+ return HasText();
+}
+
+bool AnalysisCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool do_aa) {
+ return INHERITED::clipRect(rect, op, do_aa);
+}
+
+bool AnalysisCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool do_aa) {
+ // clipPaths can make our calls to IsFullQuad invalid (ie have false
+ // positives). As a precaution, force the setting to be non-solid
+ // and non-transparent until we pop this
+ if (force_not_solid_stack_level_ == kNoLayer) {
+ force_not_solid_stack_level_ = saved_stack_size_;
+ (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
+ }
+ if (force_not_transparent_stack_level_ == kNoLayer) {
+ force_not_transparent_stack_level_ = saved_stack_size_;
+ (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
+ }
+
+ return INHERITED::clipRect(path.getBounds(), op, do_aa);
+}
+
+bool AnalysisCanvas::clipRRect(const SkRRect& rrect,
+ SkRegion::Op op,
+ bool do_aa) {
+ // clipRRect can make our calls to IsFullQuad invalid (ie have false
+ // positives). As a precaution, force the setting to be non-solid
+ // and non-transparent until we pop this
+ if (force_not_solid_stack_level_ == kNoLayer) {
+ force_not_solid_stack_level_ = saved_stack_size_;
+ (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
+ }
+ if (force_not_transparent_stack_level_ == kNoLayer) {
+ force_not_transparent_stack_level_ = saved_stack_size_;
+ (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
+ }
+
+ return INHERITED::clipRect(rrect.getBounds(), op, do_aa);
+}
+
+int AnalysisCanvas::save(SkCanvas::SaveFlags flags) {
+ ++saved_stack_size_;
+ return INHERITED::save(flags);
+}
+
+int AnalysisCanvas::saveLayer(const SkRect* bounds,
+ const SkPaint* paint,
+ SkCanvas::SaveFlags flags) {
+ ++saved_stack_size_;
+
+ // If after we draw to the saved layer, we have to blend with the current
+ // layer, then we can conservatively say that the canvas will not be of
+ // solid color.
+ if ((paint && !IsSolidColorPaint(*paint)) ||
+ (bounds && !bounds->contains(SkRect::MakeWH(getDevice()->width(),
+ getDevice()->height())))) {
+ if (force_not_solid_stack_level_ == kNoLayer) {
+ force_not_solid_stack_level_ = saved_stack_size_;
+ (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(true);
+ }
+ }
+
+ // If after we draw to the save layer, we have to blend with the current
+ // layer using any part of the current layer's alpha, then we can
+ // conservatively say that the canvas will not be transparent.
+ SkXfermode::Mode xfermode = SkXfermode::kSrc_Mode;
+ if (paint)
+ SkXfermode::AsMode(paint->getXfermode(), &xfermode);
+ if (xfermode != SkXfermode::kSrc_Mode) {
+ if (force_not_transparent_stack_level_ == kNoLayer) {
+ force_not_transparent_stack_level_ = saved_stack_size_;
+ (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(true);
+ }
+ }
+
+ // Actually saving a layer here could cause a new bitmap to be created
+ // and real rendering to occur.
+ int count = INHERITED::save(flags);
+ if (bounds) {
+ INHERITED::clipRectBounds(bounds, flags, NULL);
+ }
+ return count;
+}
+
+void AnalysisCanvas::restore() {
+ INHERITED::restore();
+
+ DCHECK(saved_stack_size_);
+ if (saved_stack_size_) {
+ --saved_stack_size_;
+ if (saved_stack_size_ < force_not_solid_stack_level_) {
+ (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotSolid(false);
+ force_not_solid_stack_level_ = kNoLayer;
+ }
+ if (saved_stack_size_ < force_not_transparent_stack_level_) {
+ (static_cast<AnalysisDevice*>(getDevice()))->SetForceNotTransparent(
+ false);
+ force_not_transparent_stack_level_ = kNoLayer;
+ }
+ }
+}
+
+} // namespace skia
+
+
diff --git a/chromium/skia/ext/analysis_canvas.h b/chromium/skia/ext/analysis_canvas.h
new file mode 100644
index 00000000000..5db1540e6af
--- /dev/null
+++ b/chromium/skia/ext/analysis_canvas.h
@@ -0,0 +1,164 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_ANALYSIS_CANVAS_H_
+#define SKIA_EXT_ANALYSIS_CANVAS_H_
+
+#include "base/compiler_specific.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkPicture.h"
+
+namespace skia {
+
+class AnalysisDevice;
+
+// Does not render anything, but gathers statistics about a region
+// (specified as a clip rectangle) of an SkPicture as the picture is
+// played back through it.
+// To use: create a SkBitmap with kNo_Config, create an AnalysisDevice
+// using that bitmap, and create an AnalysisCanvas using the device.
+// Play a picture into the canvas, and then check result.
+class SK_API AnalysisCanvas : public SkCanvas, public SkDrawPictureCallback {
+ public:
+ AnalysisCanvas(AnalysisDevice*);
+ virtual ~AnalysisCanvas();
+
+ // Returns true when a SkColor can be used to represent result.
+ bool GetColorIfSolid(SkColor* color) const;
+ bool HasText() const;
+
+ // SkDrawPictureCallback override.
+ virtual bool abortDrawing() OVERRIDE;
+
+ // SkCanvas overrides.
+ virtual bool clipRect(const SkRect& rect,
+ SkRegion::Op op = SkRegion::kIntersect_Op,
+ bool do_anti_alias = false) OVERRIDE;
+ virtual bool clipPath(const SkPath& path,
+ SkRegion::Op op = SkRegion::kIntersect_Op,
+ bool do_anti_alias = false) OVERRIDE;
+ virtual bool clipRRect(const SkRRect& rrect,
+ SkRegion::Op op = SkRegion::kIntersect_Op,
+ bool do_anti_alias = false) OVERRIDE;
+
+ virtual int saveLayer(const SkRect* bounds,
+ const SkPaint* paint,
+ SkCanvas::SaveFlags flags) OVERRIDE;
+ virtual int save(SaveFlags flags = kMatrixClip_SaveFlag) OVERRIDE;
+
+ virtual void restore() OVERRIDE;
+
+ private:
+ typedef SkCanvas INHERITED;
+
+ int saved_stack_size_;
+ int force_not_solid_stack_level_;
+ int force_not_transparent_stack_level_;
+};
+
+class SK_API AnalysisDevice : public SkDevice {
+ public:
+ AnalysisDevice(const SkBitmap& bitmap);
+ virtual ~AnalysisDevice();
+
+ bool GetColorIfSolid(SkColor* color) const;
+ bool HasText() const;
+
+ void SetForceNotSolid(bool flag);
+ void SetForceNotTransparent(bool flag);
+
+ protected:
+ // SkDevice overrides.
+ virtual void clear(SkColor color) OVERRIDE;
+ virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) OVERRIDE;
+ virtual void drawPoints(const SkDraw& draw,
+ SkCanvas::PointMode mode,
+ size_t count,
+ const SkPoint points[],
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawRect(const SkDraw& draw,
+ const SkRect& rect,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawOval(const SkDraw& draw,
+ const SkRect& oval,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawPath(const SkDraw& draw,
+ const SkPath& path,
+ const SkPaint& paint,
+ const SkMatrix* pre_path_matrix = NULL,
+ bool path_is_mutable = false) OVERRIDE;
+ virtual void drawBitmap(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkMatrix& matrix,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawSprite(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ int x,
+ int y,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawBitmapRect(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkRect* src_or_null,
+ const SkRect& dst,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawText(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawPosText(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkScalar pos[],
+ SkScalar const_y,
+ int scalars_per_pos,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawTextOnPath(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) OVERRIDE;
+#ifdef SK_BUILD_FOR_ANDROID
+ virtual void drawPosTextOnPath(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkPoint pos[],
+ const SkPaint& paint,
+ const SkPath& path,
+ const SkMatrix* matrix) OVERRIDE;
+#endif
+ virtual void drawVertices(const SkDraw& draw,
+ SkCanvas::VertexMode vertex_mode,
+ int vertex_count,
+ const SkPoint verts[],
+ const SkPoint texs[],
+ const SkColor colors[],
+ SkXfermode* xmode,
+ const uint16_t indices[],
+ int index_count,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawDevice(const SkDraw& draw,
+ SkDevice* device,
+ int x,
+ int y,
+ const SkPaint& paint) OVERRIDE;
+
+ private:
+ typedef SkDevice INHERITED;
+
+ bool is_forced_not_solid_;
+ bool is_forced_not_transparent_;
+ bool is_solid_color_;
+ SkColor color_;
+ bool is_transparent_;
+ bool has_text_;
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_ANALYSIS_CANVAS_H_
+
diff --git a/chromium/skia/ext/analysis_canvas_unittest.cc b/chromium/skia/ext/analysis_canvas_unittest.cc
new file mode 100644
index 00000000000..0b6103106ad
--- /dev/null
+++ b/chromium/skia/ext/analysis_canvas_unittest.cc
@@ -0,0 +1,406 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "skia/ext/analysis_canvas.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkShader.h"
+
+namespace {
+
+void SolidColorFill(skia::AnalysisCanvas& canvas) {
+ canvas.clear(SkColorSetARGB(255, 255, 255, 255));
+}
+
+void TransparentFill(skia::AnalysisCanvas& canvas) {
+ canvas.clear(SkColorSetARGB(0, 0, 0, 0));
+}
+
+} // namespace
+namespace skia {
+
+TEST(AnalysisCanvasTest, EmptyCanvas) {
+ SkBitmap emptyBitmap;
+ emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255);
+ skia::AnalysisDevice device(emptyBitmap);
+ skia::AnalysisCanvas canvas(&device);
+
+ SkColor color;
+ EXPECT_TRUE(canvas.GetColorIfSolid(&color));
+ EXPECT_EQ(color, SkColorSetARGB(0, 0, 0, 0));
+}
+
+TEST(AnalysisCanvasTest, ClearCanvas) {
+ SkBitmap emptyBitmap;
+ emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255);
+ skia::AnalysisDevice device(emptyBitmap);
+ skia::AnalysisCanvas canvas(&device);
+
+ // Transparent color
+ SkColor color = SkColorSetARGB(0, 12, 34, 56);
+ canvas.clear(color);
+
+ SkColor outputColor;
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+
+ // Solid color
+ color = SkColorSetARGB(255, 65, 43, 21);
+ canvas.clear(color);
+
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+ EXPECT_EQ(outputColor, color);
+
+ // Translucent color
+ color = SkColorSetARGB(128, 11, 22, 33);
+ canvas.clear(color);
+
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ // Test helper methods
+ SolidColorFill(canvas);
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+
+ TransparentFill(canvas);
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+}
+
+TEST(AnalysisCanvasTest, ComplexActions) {
+ SkBitmap emptyBitmap;
+ emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255);
+ skia::AnalysisDevice device(emptyBitmap);
+ skia::AnalysisCanvas canvas(&device);
+
+ // Draw paint test.
+ SkColor color = SkColorSetARGB(255, 11, 22, 33);
+ SkPaint paint;
+ paint.setColor(color);
+
+ canvas.drawPaint(paint);
+
+ SkColor outputColor;
+ //TODO(vmpstr): This should return true. (crbug.com/180597)
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ // Draw points test.
+ SkPoint points[4] = {
+ SkPoint::Make(0, 0),
+ SkPoint::Make(255, 0),
+ SkPoint::Make(255, 255),
+ SkPoint::Make(0, 255)
+ };
+
+ SolidColorFill(canvas);
+ canvas.drawPoints(SkCanvas::kLines_PointMode, 4, points, paint);
+
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ // Draw oval test.
+ SolidColorFill(canvas);
+ canvas.drawOval(SkRect::MakeWH(255, 255), paint);
+
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ // Draw bitmap test.
+ SolidColorFill(canvas);
+ SkBitmap secondBitmap;
+ secondBitmap.setConfig(SkBitmap::kNo_Config, 255, 255);
+ canvas.drawBitmap(secondBitmap, 0, 0);
+
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+}
+
+TEST(AnalysisCanvasTest, SimpleDrawRect) {
+ SkBitmap emptyBitmap;
+ emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255);
+ skia::AnalysisDevice device(emptyBitmap);
+ skia::AnalysisCanvas canvas(&device);
+
+ SkColor color = SkColorSetARGB(255, 11, 22, 33);
+ SkPaint paint;
+ paint.setColor(color);
+ canvas.clipRect(SkRect::MakeWH(255, 255));
+ canvas.drawRect(SkRect::MakeWH(255, 255), paint);
+
+ SkColor outputColor;
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+ EXPECT_EQ(color, outputColor);
+
+ color = SkColorSetARGB(255, 22, 33, 44);
+ paint.setColor(color);
+ canvas.translate(-128, -128);
+ canvas.drawRect(SkRect::MakeWH(382, 382), paint);
+
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ color = SkColorSetARGB(255, 33, 44, 55);
+ paint.setColor(color);
+ canvas.drawRect(SkRect::MakeWH(383, 383), paint);
+
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+ EXPECT_EQ(color, outputColor);
+
+ color = SkColorSetARGB(0, 0, 0, 0);
+ paint.setColor(color);
+ canvas.drawRect(SkRect::MakeWH(383, 383), paint);
+
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+ EXPECT_EQ(outputColor, SkColorSetARGB(255, 33, 44, 55));
+
+ color = SkColorSetARGB(128, 128, 128, 128);
+ paint.setColor(color);
+ canvas.drawRect(SkRect::MakeWH(383, 383), paint);
+
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ paint.setXfermodeMode(SkXfermode::kClear_Mode);
+ canvas.drawRect(SkRect::MakeWH(382, 382), paint);
+
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ canvas.drawRect(SkRect::MakeWH(383, 383), paint);
+
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+
+ canvas.translate(128, 128);
+ color = SkColorSetARGB(255, 11, 22, 33);
+ paint.setColor(color);
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+ canvas.drawRect(SkRect::MakeWH(255, 255), paint);
+
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+ EXPECT_EQ(color, outputColor);
+
+ canvas.rotate(50);
+ canvas.drawRect(SkRect::MakeWH(255, 255), paint);
+
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+}
+
+TEST(AnalysisCanvasTest, ClipPath) {
+ SkBitmap emptyBitmap;
+ emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255);
+ skia::AnalysisDevice device(emptyBitmap);
+ skia::AnalysisCanvas canvas(&device);
+
+ SkPath path;
+ path.moveTo(0, 0);
+ path.lineTo(255, 0);
+ path.lineTo(255, 255);
+ path.lineTo(0, 255);
+
+ SkColor outputColor;
+ SolidColorFill(canvas);
+ canvas.clipPath(path);
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ canvas.save();
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ canvas.clipPath(path);
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ canvas.restore();
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ SolidColorFill(canvas);
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+}
+
+TEST(AnalysisCanvasTest, SaveLayerRestore) {
+ SkBitmap emptyBitmap;
+ emptyBitmap.setConfig(SkBitmap::kNo_Config, 255, 255);
+ skia::AnalysisDevice device(emptyBitmap);
+ skia::AnalysisCanvas canvas(&device);
+
+ SkColor outputColor;
+ SolidColorFill(canvas);
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+
+ SkRect bounds = SkRect::MakeWH(255, 255);
+ SkPaint paint;
+ paint.setColor(SkColorSetARGB(255, 255, 255, 255));
+ paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
+
+ // This should force non-transparency
+ canvas.saveLayer(&bounds, &paint, SkCanvas::kMatrix_SaveFlag);
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+
+ TransparentFill(canvas);
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ SolidColorFill(canvas);
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+
+ paint.setXfermodeMode(SkXfermode::kDst_Mode);
+
+ // This should force non-solid color
+ canvas.saveLayer(&bounds, &paint, SkCanvas::kMatrix_SaveFlag);
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ TransparentFill(canvas);
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ SolidColorFill(canvas);
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ canvas.restore();
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ TransparentFill(canvas);
+ EXPECT_FALSE(canvas.GetColorIfSolid(&outputColor));
+
+ SolidColorFill(canvas);
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+
+ canvas.restore();
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+
+ TransparentFill(canvas);
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+
+ SolidColorFill(canvas);
+ EXPECT_TRUE(canvas.GetColorIfSolid(&outputColor));
+ EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), outputColor);
+}
+
+TEST(AnalysisCanvasTest, HasText) {
+ int width = 200;
+ int height = 100;
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kNo_Config, width, height);
+
+ const char* text = "A";
+ size_t byteLength = 1;
+
+ SkPoint point = SkPoint::Make(SkIntToScalar(25), SkIntToScalar(25));
+ SkPath path;
+ path.moveTo(point);
+ path.lineTo(SkIntToScalar(75), SkIntToScalar(75));
+
+ SkPaint paint;
+ paint.setColor(SK_ColorGRAY);
+ paint.setTextSize(SkIntToScalar(10));
+
+ {
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ // Test after initialization.
+ EXPECT_FALSE(canvas.HasText());
+ // Test drawing anything other than text.
+ canvas.drawRect(SkRect::MakeWH(width/2, height), paint);
+ EXPECT_FALSE(canvas.HasText());
+ }
+ {
+ // Test SkCanvas::drawText.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawText(text, byteLength, point.fX, point.fY, paint);
+ EXPECT_TRUE(canvas.HasText());
+ }
+ {
+ // Test SkCanvas::drawPosText.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawPosText(text, byteLength, &point, paint);
+ EXPECT_TRUE(canvas.HasText());
+ }
+ {
+ // Test SkCanvas::drawPosTextH.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawPosTextH(text, byteLength, &point.fX, point.fY, paint);
+ EXPECT_TRUE(canvas.HasText());
+ }
+ {
+ // Test SkCanvas::drawTextOnPathHV.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawTextOnPathHV(text, byteLength, path, point.fX, point.fY, paint);
+ EXPECT_TRUE(canvas.HasText());
+ }
+ {
+ // Test SkCanvas::drawTextOnPath.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawTextOnPath(text, byteLength, path, NULL, paint);
+ EXPECT_TRUE(canvas.HasText());
+ }
+ {
+ // Text under opaque rect.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawText(text, byteLength, point.fX, point.fY, paint);
+ EXPECT_TRUE(canvas.HasText());
+ canvas.drawRect(SkRect::MakeWH(width, height), paint);
+ EXPECT_FALSE(canvas.HasText());
+ }
+ {
+ // Text under translucent rect.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawText(text, byteLength, point.fX, point.fY, paint);
+ EXPECT_TRUE(canvas.HasText());
+ SkPaint translucentPaint;
+ translucentPaint.setColor(0x88FFFFFF);
+ canvas.drawRect(SkRect::MakeWH(width, height), translucentPaint);
+ EXPECT_TRUE(canvas.HasText());
+ }
+ {
+ // Text under rect in clear mode.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawText(text, byteLength, point.fX, point.fY, paint);
+ EXPECT_TRUE(canvas.HasText());
+ SkPaint clearModePaint;
+ clearModePaint.setXfermodeMode(SkXfermode::kClear_Mode);
+ canvas.drawRect(SkRect::MakeWH(width, height), clearModePaint);
+ EXPECT_FALSE(canvas.HasText());
+ }
+ {
+ // Clear.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.drawText(text, byteLength, point.fX, point.fY, paint);
+ EXPECT_TRUE(canvas.HasText());
+ canvas.clear(SK_ColorGRAY);
+ EXPECT_FALSE(canvas.HasText());
+ }
+ {
+ // Text inside clip region.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.clipRect(SkRect::MakeWH(100, 100));
+ canvas.drawText(text, byteLength, point.fX, point.fY, paint);
+ EXPECT_TRUE(canvas.HasText());
+ }
+ {
+ // Text outside clip region.
+ skia::AnalysisDevice device(bitmap);
+ skia::AnalysisCanvas canvas(&device);
+ canvas.clipRect(SkRect::MakeXYWH(100, 0, 100, 100));
+ canvas.drawText(text, byteLength, point.fX, point.fY, paint);
+ // Analysis device does not do any clipping.
+ // So even when text is outside the clip region,
+ // it is marked as having the text.
+ // TODO(alokp): We may be able to do some trivial rejection.
+ EXPECT_TRUE(canvas.HasText());
+ }
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/benchmarking_canvas.cc b/chromium/skia/ext/benchmarking_canvas.cc
new file mode 100644
index 00000000000..d83252b4c55
--- /dev/null
+++ b/chromium/skia/ext/benchmarking_canvas.cc
@@ -0,0 +1,238 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/containers/hash_tables.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "skia/ext/benchmarking_canvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/utils/SkProxyCanvas.h"
+
+namespace skia {
+
+class AutoStamper {
+public:
+ AutoStamper(TimingCanvas* timing_canvas);
+ ~AutoStamper();
+
+private:
+ TimingCanvas* timing_canvas_;
+ base::TimeTicks start_ticks_;
+};
+
+class TimingCanvas : public SkProxyCanvas {
+public:
+ TimingCanvas(int width, int height, const BenchmarkingCanvas* track_canvas)
+ : tracking_canvas_(track_canvas) {
+ skia::RefPtr<SkDevice> device = skia::AdoptRef(
+ SkNEW_ARGS(SkDevice, (SkBitmap::kARGB_8888_Config, width, height)));
+ canvas_ = skia::AdoptRef(SkNEW_ARGS(SkCanvas, (device.get())));
+
+ setProxy(canvas_.get());
+ }
+
+ virtual ~TimingCanvas() {
+ }
+
+ double GetTime(size_t index) {
+ TimingsMap::const_iterator timing_info = timings_map_.find(index);
+ return timing_info != timings_map_.end()
+ ? timing_info->second.InMillisecondsF()
+ : 0.0;
+ }
+
+ // SkCanvas overrides.
+ virtual int save(SaveFlags flags = kMatrixClip_SaveFlag) OVERRIDE {
+ AutoStamper stamper(this);
+ return SkProxyCanvas::save(flags);
+ }
+
+ virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags = kARGB_ClipLayer_SaveFlag) OVERRIDE {
+ AutoStamper stamper(this);
+ return SkProxyCanvas::saveLayer(bounds, paint, flags);
+ }
+
+ virtual void restore() OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::restore();
+ }
+
+ virtual bool clipRect(const SkRect& rect, SkRegion::Op op,
+ bool doAa) OVERRIDE {
+ AutoStamper stamper(this);
+ return SkProxyCanvas::clipRect(rect, op, doAa);
+ }
+
+ virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op,
+ bool doAa) OVERRIDE {
+ AutoStamper stamper(this);
+ return SkProxyCanvas::clipRRect(rrect, op, doAa);
+ }
+
+ virtual bool clipPath(const SkPath& path, SkRegion::Op op,
+ bool doAa) OVERRIDE {
+ AutoStamper stamper(this);
+ return SkProxyCanvas::clipPath(path, op, doAa);
+ }
+
+ virtual bool clipRegion(const SkRegion& region,
+ SkRegion::Op op = SkRegion::kIntersect_Op) OVERRIDE {
+ AutoStamper stamper(this);
+ return SkProxyCanvas::clipRegion(region, op);
+ }
+
+ virtual void drawPaint(const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawPaint(paint);
+ }
+
+ virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawPoints(mode, count, pts, paint);
+ }
+
+ virtual void drawOval(const SkRect& rect, const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawOval(rect, paint);
+ }
+
+ virtual void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawRect(rect, paint);
+ }
+
+ virtual void drawRRect(const SkRRect& rrect, const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawRRect(rrect, paint);
+ }
+
+ virtual void drawPath(const SkPath& path, const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawPath(path, paint);
+ }
+
+ virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+ const SkPaint* paint = NULL) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawBitmap(bitmap, left, top, paint);
+ }
+
+ virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+ const SkRect& dst,
+ const SkPaint* paint = NULL) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawBitmapRectToRect(bitmap, src, dst, paint);
+ }
+
+ virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
+ const SkPaint* paint = NULL) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawBitmapMatrix(bitmap, m, paint);
+ }
+
+ virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+ const SkPaint* paint = NULL) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawSprite(bitmap, left, top, paint);
+ }
+
+ virtual void drawText(const void* text, size_t byteLength, SkScalar x,
+ SkScalar y, const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawText(text, byteLength, x, y, paint);
+ }
+
+ virtual void drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[],
+ const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawPosText(text, byteLength, pos, paint);
+ }
+
+ virtual void drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawPosTextH(text, byteLength, xpos, constY, paint);
+ }
+
+ virtual void drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawTextOnPath(text, byteLength, path, matrix, paint);
+ }
+
+ virtual void drawPicture(SkPicture& picture) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawPicture(picture);
+ }
+
+ virtual void drawVertices(VertexMode vmode, int vertexCount,
+ const SkPoint vertices[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawVertices(vmode, vertexCount, vertices, texs, colors,
+ xmode, indices, indexCount, paint);
+ }
+
+ virtual void drawData(const void* data, size_t length) OVERRIDE {
+ AutoStamper stamper(this);
+ SkProxyCanvas::drawData(data, length);
+ }
+
+private:
+ typedef base::hash_map<size_t, base::TimeDelta> TimingsMap;
+ TimingsMap timings_map_;
+
+ skia::RefPtr<SkCanvas> canvas_;
+
+ friend class AutoStamper;
+ const BenchmarkingCanvas* tracking_canvas_;
+};
+
+AutoStamper::AutoStamper(TimingCanvas *timing_canvas)
+ : timing_canvas_(timing_canvas) {
+ start_ticks_ = base::TimeTicks::HighResNow();
+}
+
+AutoStamper::~AutoStamper() {
+ base::TimeDelta delta = base::TimeTicks::HighResNow() - start_ticks_;
+ int command_index = timing_canvas_->tracking_canvas_->CommandCount() - 1;
+ DCHECK_GE(command_index, 0);
+ timing_canvas_->timings_map_[command_index] = delta;
+}
+
+BenchmarkingCanvas::BenchmarkingCanvas(int width, int height)
+ : SkNWayCanvas(width, height) {
+ debug_canvas_ = skia::AdoptRef(SkNEW_ARGS(SkDebugCanvas, (width, height)));
+ timing_canvas_ = skia::AdoptRef(SkNEW_ARGS(TimingCanvas, (width, height, this)));
+
+ addCanvas(debug_canvas_.get());
+ addCanvas(timing_canvas_.get());
+}
+
+BenchmarkingCanvas::~BenchmarkingCanvas() {
+ removeAll();
+}
+
+size_t BenchmarkingCanvas::CommandCount() const {
+ return debug_canvas_->getSize();
+}
+
+SkDrawCommand* BenchmarkingCanvas::GetCommand(size_t index) {
+ DCHECK_LT(index, static_cast<size_t>(debug_canvas_->getSize()));
+ return debug_canvas_->getDrawCommandAt(index);
+}
+
+double BenchmarkingCanvas::GetTime(size_t index) {
+ DCHECK_LT(index, static_cast<size_t>(debug_canvas_->getSize()));
+ return timing_canvas_->GetTime(index);
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/benchmarking_canvas.h b/chromium/skia/ext/benchmarking_canvas.h
new file mode 100644
index 00000000000..7ef82049b2c
--- /dev/null
+++ b/chromium/skia/ext/benchmarking_canvas.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_BENCHMARKING_CANVAS_H_
+#define SKIA_EXT_BENCHMARKING_CANVAS_H_
+
+#include "base/compiler_specific.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/utils/SkNWayCanvas.h"
+#include "third_party/skia/src/utils/debugger/SkDebugCanvas.h"
+
+namespace skia {
+
+class TimingCanvas;
+
+class SK_API BenchmarkingCanvas : public SkNWayCanvas {
+public:
+ BenchmarkingCanvas(int width, int height);
+ virtual ~BenchmarkingCanvas();
+
+ // Returns the number of draw commands executed on this canvas.
+ size_t CommandCount() const;
+
+ // Get draw command info for a given index.
+ SkDrawCommand* GetCommand(size_t index);
+
+ // Return the recorded render time (milliseconds) for a draw command index.
+ double GetTime(size_t index);
+
+private:
+ // In order to avoid introducing a Skia version dependency, this
+ // implementation dispatches draw commands in lock-step to two distinct
+ // canvases:
+ // * a SkDebugCanvas used for gathering command info and tracking
+ // the current command index
+ // * a SkiaTimingCanvas used for measuring raster paint times (and relying
+ // on the former for tracking the current command index).
+ //
+ // This way, if the SkCanvas API is extended, we don't need to worry about
+ // updating content::SkiaTimingCanvas to accurately override all new methods
+ // (to avoid timing info indices from getting out of sync), as SkDebugCanvas
+ // already does that for us.
+
+ skia::RefPtr<SkDebugCanvas> debug_canvas_;
+ skia::RefPtr<TimingCanvas> timing_canvas_;
+};
+
+}
+#endif // SKIA_EXT_BENCHMARKING_CANVAS_H
diff --git a/chromium/skia/ext/bitmap_platform_device.h b/chromium/skia/ext/bitmap_platform_device.h
new file mode 100644
index 00000000000..56e41763f8d
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_H_
+#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_H_
+
+// This file provides an easy way to include the appropriate
+// BitmapPlatformDevice header file for your platform.
+
+#if defined(WIN32)
+#include "skia/ext/bitmap_platform_device_win.h"
+#elif defined(__APPLE__)
+#include "skia/ext/bitmap_platform_device_mac.h"
+#elif defined(ANDROID)
+#include "skia/ext/bitmap_platform_device_android.h"
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun)
+#include "skia/ext/bitmap_platform_device_linux.h"
+#endif
+
+namespace skia {
+ // Returns true if it is unsafe to attempt to allocate an offscreen buffer
+ // given these dimensions.
+ inline bool RasterDeviceTooBigToAllocate(int width, int height) {
+
+#ifndef SKIA_EXT_RASTER_DEVICE_ALLOCATION_MAX
+#define SKIA_EXT_RASTER_DEVICE_ALLOCATION_MAX (2 * 256 * 1024 * 1024)
+#endif
+
+ int bytesPerPixel = 4;
+ int64_t bytes = (int64_t)width * height * bytesPerPixel;
+ return bytes > SKIA_EXT_RASTER_DEVICE_ALLOCATION_MAX;
+ }
+}
+
+#endif // SKIA_EXT_BITMAP_PLATFORM_DEVICE_H_
diff --git a/chromium/skia/ext/bitmap_platform_device_android.cc b/chromium/skia/ext/bitmap_platform_device_android.cc
new file mode 100644
index 00000000000..8a29586abcb
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_android.cc
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/bitmap_platform_device_android.h"
+#include "skia/ext/platform_canvas.h"
+
+namespace skia {
+
+BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
+ bool is_opaque) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ if (bitmap.allocPixels()) {
+ bitmap.setIsOpaque(is_opaque);
+ // Follow the logic in SkCanvas::createDevice(), initialize the bitmap if it
+ // is not opaque.
+ if (!is_opaque)
+ bitmap.eraseARGB(0, 0, 0, 0);
+ return new BitmapPlatformDevice(bitmap);
+ }
+ return NULL;
+}
+
+BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
+ int height,
+ bool is_opaque) {
+ BitmapPlatformDevice* device = Create(width, height, is_opaque);
+ if (!is_opaque)
+ device->accessBitmap(true).eraseARGB(0, 0, 0, 0);
+ return device;
+}
+
+BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
+ bool is_opaque,
+ uint8_t* data) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ if (data)
+ bitmap.setPixels(data);
+ else if (!bitmap.allocPixels())
+ return NULL;
+
+ bitmap.setIsOpaque(is_opaque);
+ return new BitmapPlatformDevice(bitmap);
+}
+
+BitmapPlatformDevice::BitmapPlatformDevice(const SkBitmap& bitmap)
+ : SkDevice(bitmap) {
+ SetPlatformDevice(this, this);
+}
+
+BitmapPlatformDevice::~BitmapPlatformDevice() {
+}
+
+SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
+ SkBitmap::Config config, int width, int height, bool isOpaque,
+ Usage /*usage*/) {
+ SkASSERT(config == SkBitmap::kARGB_8888_Config);
+ return BitmapPlatformDevice::Create(width, height, isOpaque);
+}
+
+PlatformSurface BitmapPlatformDevice::BeginPlatformPaint() {
+ // TODO(zhenghao): What should we return? The ptr to the address of the
+ // pixels? Maybe this won't be called at all.
+ return accessBitmap(true).getPixels();
+}
+
+void BitmapPlatformDevice::DrawToNativeContext(
+ PlatformSurface surface, int x, int y, const PlatformRect* src_rect) {
+ // Should never be called on Android.
+ SkASSERT(false);
+}
+
+// PlatformCanvas impl
+
+SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
+ uint8_t* data, OnFailureType failureType) {
+ skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+ BitmapPlatformDevice::Create(width, height, is_opaque, data));
+ return CreateCanvas(dev, failureType);
+}
+
+// Port of PlatformBitmap to android
+PlatformBitmap::~PlatformBitmap() {
+ // Nothing to do.
+}
+
+bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
+ bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ if (!bitmap_.allocPixels())
+ return false;
+
+ bitmap_.setIsOpaque(is_opaque);
+ surface_ = bitmap_.getPixels();
+ return true;
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/bitmap_platform_device_android.h b/chromium/skia/ext/bitmap_platform_device_android.h
new file mode 100644
index 00000000000..b5755cb4b0b
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_android.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_ANDROID_H_
+#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_ANDROID_H_
+
+#include "base/memory/ref_counted.h"
+#include "base/compiler_specific.h"
+#include "skia/ext/platform_device.h"
+
+namespace skia {
+
+// -----------------------------------------------------------------------------
+// For now we just use SkBitmap for SkDevice
+//
+// This is all quite ok for test_shell. In the future we will want to use
+// shared memory between the renderer and the main process at least. In this
+// case we'll probably create the buffer from a precreated region of memory.
+// -----------------------------------------------------------------------------
+class BitmapPlatformDevice : public SkDevice, public PlatformDevice {
+ public:
+ // Construct a BitmapPlatformDevice. |is_opaque| should be set if the caller
+ // knows the bitmap will be completely opaque and allows some optimizations.
+ // The bitmap is not initialized.
+ static BitmapPlatformDevice* Create(int width, int height, bool is_opaque);
+
+ // Construct a BitmapPlatformDevice, as above.
+ // If |is_opaque| is false, the bitmap is initialized to 0.
+ static BitmapPlatformDevice* CreateAndClear(int width, int height,
+ bool is_opaque);
+
+ // This doesn't take ownership of |data|. If |data| is null, the bitmap
+ // is not initialized to 0.
+ static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
+ uint8_t* data);
+
+ // Create a BitmapPlatformDevice from an already constructed bitmap;
+ // you should probably be using Create(). This may become private later if
+ // we ever have to share state between some native drawing UI and Skia, like
+ // the Windows and Mac versions of this class do.
+ explicit BitmapPlatformDevice(const SkBitmap& other);
+ virtual ~BitmapPlatformDevice();
+
+ virtual PlatformSurface BeginPlatformPaint() OVERRIDE;
+ virtual void DrawToNativeContext(PlatformSurface surface, int x, int y,
+ const PlatformRect* src_rect) OVERRIDE;
+
+ protected:
+ virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+ int height, bool isOpaque,
+ Usage usage) OVERRIDE;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_BITMAP_PLATFORM_DEVICE_ANDROID_H_
diff --git a/chromium/skia/ext/bitmap_platform_device_data.h b/chromium/skia/ext/bitmap_platform_device_data.h
new file mode 100644
index 00000000000..81e81ed79ff
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_data.h
@@ -0,0 +1,102 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_DATA_H_
+#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_DATA_H_
+
+#include "skia/ext/bitmap_platform_device.h"
+
+namespace skia {
+
+class BitmapPlatformDevice::BitmapPlatformDeviceData :
+#if defined(WIN32) || defined(__APPLE__)
+ public SkRefCnt {
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun)
+ // These objects are reference counted and own a Cairo surface. The surface
+ // is the backing store for a Skia bitmap and we reference count it so that
+ // we can copy BitmapPlatformDevice objects without having to copy all the
+ // image data.
+ public base::RefCounted<BitmapPlatformDeviceData> {
+#endif
+
+ public:
+#if defined(WIN32)
+ typedef HBITMAP PlatformContext;
+#elif defined(__APPLE__)
+ typedef CGContextRef PlatformContext;
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun)
+ typedef cairo_t* PlatformContext;
+#endif
+
+#if defined(WIN32) || defined(__APPLE__)
+ explicit BitmapPlatformDeviceData(PlatformContext bitmap);
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun)
+ explicit BitmapPlatformDeviceData(cairo_surface_t* surface);
+#endif
+
+#if defined(WIN32)
+ // Create/destroy hdc_, which is the memory DC for our bitmap data.
+ HDC GetBitmapDC();
+ void ReleaseBitmapDC();
+ bool IsBitmapDCCreated() const;
+#endif
+
+#if defined(__APPLE__)
+ void ReleaseBitmapContext();
+#endif // defined(__APPLE__)
+
+ // Sets the transform and clip operations. This will not update the CGContext,
+ // but will mark the config as dirty. The next call of LoadConfig will
+ // pick up these changes.
+ void SetMatrixClip(const SkMatrix& transform, const SkRegion& region);
+
+ // Loads the current transform and clip into the context. Can be called even
+ // when |bitmap_context_| is NULL (will be a NOP).
+ void LoadConfig();
+
+ const SkMatrix& transform() const {
+ return transform_;
+ }
+
+ PlatformContext bitmap_context() {
+ return bitmap_context_;
+ }
+
+ private:
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun)
+ friend class base::RefCounted<BitmapPlatformDeviceData>;
+#endif
+ virtual ~BitmapPlatformDeviceData();
+
+ // Lazily-created graphics context used to draw into the bitmap.
+ PlatformContext bitmap_context_;
+
+#if defined(WIN32)
+ // Lazily-created DC used to draw into the bitmap, see GetBitmapDC().
+ HDC hdc_;
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__sun)
+ cairo_surface_t *const surface_;
+#endif
+
+ // True when there is a transform or clip that has not been set to the
+ // context. The context is retrieved for every text operation, and the
+ // transform and clip do not change as much. We can save time by not loading
+ // the clip and transform for every one.
+ bool config_dirty_;
+
+ // Translation assigned to the context: we need to keep track of this
+ // separately so it can be updated even if the context isn't created yet.
+ SkMatrix transform_;
+
+ // The current clipping
+ SkRegion clip_region_;
+
+ // Disallow copy & assign.
+ BitmapPlatformDeviceData(const BitmapPlatformDeviceData&);
+ BitmapPlatformDeviceData& operator=(const BitmapPlatformDeviceData&);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_BITMAP_PLATFORM_DEVICE_DATA_H_
diff --git a/chromium/skia/ext/bitmap_platform_device_linux.cc b/chromium/skia/ext/bitmap_platform_device_linux.cc
new file mode 100644
index 00000000000..c9a02148d8e
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_linux.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/bitmap_platform_device_linux.h"
+#include "skia/ext/bitmap_platform_device_data.h"
+#include "skia/ext/platform_canvas.h"
+
+#if defined(OS_OPENBSD)
+#include <cairo.h>
+#else
+#include <cairo/cairo.h>
+#endif
+
+namespace skia {
+
+namespace {
+
+void LoadMatrixToContext(cairo_t* context, const SkMatrix& matrix) {
+ cairo_matrix_t cairo_matrix;
+ cairo_matrix_init(&cairo_matrix,
+ SkScalarToFloat(matrix.getScaleX()),
+ SkScalarToFloat(matrix.getSkewY()),
+ SkScalarToFloat(matrix.getSkewX()),
+ SkScalarToFloat(matrix.getScaleY()),
+ SkScalarToFloat(matrix.getTranslateX()),
+ SkScalarToFloat(matrix.getTranslateY()));
+ cairo_set_matrix(context, &cairo_matrix);
+}
+
+void LoadClipToContext(cairo_t* context, const SkRegion& clip) {
+ cairo_reset_clip(context);
+
+ // TODO(brettw) support non-rect clips.
+ SkIRect bounding = clip.getBounds();
+ cairo_rectangle(context, bounding.fLeft, bounding.fTop,
+ bounding.fRight - bounding.fLeft,
+ bounding.fBottom - bounding.fTop);
+ cairo_clip(context);
+}
+
+} // namespace
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
+ cairo_surface_t* surface)
+ : surface_(surface),
+ config_dirty_(true),
+ transform_(SkMatrix::I()) { // Want to load the config next time.
+ bitmap_context_ = cairo_create(surface);
+}
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
+ cairo_destroy(bitmap_context_);
+ cairo_surface_destroy(surface_);
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
+ const SkMatrix& transform,
+ const SkRegion& region) {
+ transform_ = transform;
+ clip_region_ = region;
+ config_dirty_ = true;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
+ if (!config_dirty_ || !bitmap_context_)
+ return; // Nothing to do.
+ config_dirty_ = false;
+
+ // Load the identity matrix since this is what our clip is relative to.
+ cairo_matrix_t cairo_matrix;
+ cairo_matrix_init_identity(&cairo_matrix);
+ cairo_set_matrix(bitmap_context_, &cairo_matrix);
+
+ LoadClipToContext(bitmap_context_, clip_region_);
+ LoadMatrixToContext(bitmap_context_, transform_);
+}
+
+// We use this static factory function instead of the regular constructor so
+// that we can create the pixel data before calling the constructor. This is
+// required so that we can call the base class' constructor with the pixel
+// data.
+BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
+ bool is_opaque,
+ cairo_surface_t* surface) {
+ if (cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(surface);
+ return NULL;
+ }
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height,
+ cairo_image_surface_get_stride(surface));
+ bitmap.setPixels(cairo_image_surface_get_data(surface));
+ bitmap.setIsOpaque(is_opaque);
+
+ // The device object will take ownership of the graphics context.
+ return new BitmapPlatformDevice
+ (bitmap, new BitmapPlatformDeviceData(surface));
+}
+
+BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
+ bool is_opaque) {
+ // This initializes the bitmap to all zeros.
+ cairo_surface_t* surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ width, height);
+
+ BitmapPlatformDevice* device = Create(width, height, is_opaque, surface);
+
+#ifndef NDEBUG
+ if (device && is_opaque) // Fill with bright bluish green
+ device->eraseColor(SkColorSetARGB(255, 0, 255, 128));
+#endif
+
+ return device;
+}
+
+BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
+ int height,
+ bool is_opaque) {
+ // The Linux port always constructs initialized bitmaps, so there is no extra
+ // work to perform here.
+ return Create(width, height, is_opaque);
+}
+
+BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
+ bool is_opaque,
+ uint8_t* data) {
+ cairo_surface_t* surface = cairo_image_surface_create_for_data(
+ data, CAIRO_FORMAT_ARGB32, width, height,
+ cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width));
+
+ return Create(width, height, is_opaque, surface);
+}
+
+// The device will own the bitmap, which corresponds to also owning the pixel
+// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
+BitmapPlatformDevice::BitmapPlatformDevice(
+ const SkBitmap& bitmap,
+ BitmapPlatformDeviceData* data)
+ : SkDevice(bitmap),
+ data_(data) {
+ SetPlatformDevice(this, this);
+}
+
+BitmapPlatformDevice::~BitmapPlatformDevice() {
+}
+
+SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
+ SkBitmap::Config config, int width, int height, bool isOpaque,
+ Usage /*usage*/) {
+ SkASSERT(config == SkBitmap::kARGB_8888_Config);
+ return BitmapPlatformDevice::Create(width, height, isOpaque);
+}
+
+cairo_t* BitmapPlatformDevice::BeginPlatformPaint() {
+ data_->LoadConfig();
+ cairo_t* cairo = data_->bitmap_context();
+ cairo_surface_t* surface = cairo_get_target(cairo);
+ // Tell cairo to flush anything it has pending.
+ cairo_surface_flush(surface);
+ // Tell Cairo that we (probably) modified (actually, will modify) its pixel
+ // buffer directly.
+ cairo_surface_mark_dirty(surface);
+ return cairo;
+}
+
+void BitmapPlatformDevice::DrawToNativeContext(
+ PlatformSurface surface, int x, int y, const PlatformRect* src_rect) {
+ // Should never be called on Linux.
+ SkASSERT(false);
+}
+
+void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
+ const SkRegion& region,
+ const SkClipStack&) {
+ data_->SetMatrixClip(transform, region);
+}
+
+// PlatformCanvas impl
+
+SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
+ uint8_t* data, OnFailureType failureType) {
+ skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+ BitmapPlatformDevice::Create(width, height, is_opaque, data));
+ return CreateCanvas(dev, failureType);
+}
+
+// Port of PlatformBitmap to linux
+PlatformBitmap::~PlatformBitmap() {
+ cairo_destroy(surface_);
+}
+
+bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
+ // The SkBitmap allocates and owns the bitmap memory; PlatformBitmap owns the
+ // cairo drawing context tied to the bitmap. The SkBitmap's pixelRef can
+ // outlive the PlatformBitmap if additional copies are made.
+ int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
+ bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height, stride);
+ if (!bitmap_.allocPixels()) // Using the default allocator.
+ return false;
+ bitmap_.setIsOpaque(is_opaque);
+
+ cairo_surface_t* surf = cairo_image_surface_create_for_data(
+ reinterpret_cast<unsigned char*>(bitmap_.getPixels()),
+ CAIRO_FORMAT_ARGB32,
+ width,
+ height,
+ stride);
+ if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(surf);
+ return false;
+ }
+
+ surface_ = cairo_create(surf);
+ cairo_surface_destroy(surf);
+ return true;
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/bitmap_platform_device_linux.h b/chromium/skia/ext/bitmap_platform_device_linux.h
new file mode 100644
index 00000000000..e1f9a75c850
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_linux.h
@@ -0,0 +1,114 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_LINUX_H_
+#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_LINUX_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "skia/ext/platform_device.h"
+
+typedef struct _cairo_surface cairo_surface_t;
+
+// -----------------------------------------------------------------------------
+// Image byte ordering on Linux:
+//
+// Pixels are packed into 32-bit words these days. Even for 24-bit images,
+// often 8-bits will be left unused for alignment reasons. Thus, when you see
+// ARGB as the byte order you have to wonder if that's in memory order or
+// little-endian order. Here I'll write A.R.G.B to specifiy the memory order.
+//
+// GdkPixbuf's provide a nice backing store and defaults to R.G.B.A order.
+// They'll do the needed byte swapping to match the X server when drawn.
+//
+// Skia can be controled in skia/include/corecg/SkUserConfig.h (see bits about
+// SK_R32_SHIFT). For Linux we define it to be ARGB in registers. For little
+// endian machines that means B.G.R.A in memory.
+//
+// The image loaders are controlled in
+// webkit/port/platform/image-decoders/ImageDecoder.h (see setRGBA). These are
+// also configured for ARGB in registers.
+//
+// Cairo's only 32-bit mode is ARGB in registers.
+//
+// X servers commonly have a 32-bit visual with xRGB in registers (since they
+// typically don't do alpha blending of drawables at the user level. Composite
+// extensions aside.)
+//
+// We don't use GdkPixbuf because its byte order differs from the rest. Most
+// importantly, it differs from Cairo which, being a system library, is
+// something that we can't easily change.
+// -----------------------------------------------------------------------------
+
+namespace skia {
+
+// -----------------------------------------------------------------------------
+// This is the Linux bitmap backing for Skia. We create a Cairo image surface
+// to store the backing buffer. This buffer is BGRA in memory (on little-endian
+// machines).
+//
+// For now we are also using Cairo to paint to the Drawables so we provide an
+// accessor for getting the surface.
+//
+// This is all quite ok for test_shell. In the future we will want to use
+// shared memory between the renderer and the main process at least. In this
+// case we'll probably create the buffer from a precreated region of memory.
+// -----------------------------------------------------------------------------
+class BitmapPlatformDevice : public SkDevice, public PlatformDevice {
+ // A reference counted cairo surface
+ class BitmapPlatformDeviceData;
+
+ public:
+ // Create a BitmapPlatformDeviceLinux from an already constructed bitmap;
+ // you should probably be using Create(). This may become private later if
+ // we ever have to share state between some native drawing UI and Skia, like
+ // the Windows and Mac versions of this class do.
+ //
+ // This object takes ownership of @data.
+ BitmapPlatformDevice(const SkBitmap& other, BitmapPlatformDeviceData* data);
+ virtual ~BitmapPlatformDevice();
+
+ // Constructs a device with size |width| * |height| with contents initialized
+ // to zero. |is_opaque| should be set if the caller knows the bitmap will be
+ // completely opaque and allows some optimizations.
+ static BitmapPlatformDevice* Create(int width, int height, bool is_opaque);
+
+ // Performs the same construction as Create.
+ // Other ports require a separate construction routine because Create does not
+ // initialize the bitmap to 0.
+ static BitmapPlatformDevice* CreateAndClear(int width, int height,
+ bool is_opaque);
+
+ // This doesn't take ownership of |data|. If |data| is NULL, the contents
+ // of the device are initialized to 0.
+ static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
+ uint8_t* data);
+
+ // Overridden from SkDevice:
+ virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
+ const SkClipStack&) OVERRIDE;
+
+ // Overridden from PlatformDevice:
+ virtual cairo_t* BeginPlatformPaint() OVERRIDE;
+ virtual void DrawToNativeContext(PlatformSurface surface, int x, int y,
+ const PlatformRect* src_rect) OVERRIDE;
+
+ protected:
+ virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+ int height, bool isOpaque,
+ Usage usage) OVERRIDE;
+
+ private:
+ static BitmapPlatformDevice* Create(int width, int height, bool is_opaque,
+ cairo_surface_t* surface);
+
+ scoped_refptr<BitmapPlatformDeviceData> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_BITMAP_PLATFORM_DEVICE_LINUX_H_
diff --git a/chromium/skia/ext/bitmap_platform_device_mac.cc b/chromium/skia/ext/bitmap_platform_device_mac.cc
new file mode 100644
index 00000000000..b7b05e50a3c
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_mac.cc
@@ -0,0 +1,303 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/bitmap_platform_device_mac.h"
+
+#import <ApplicationServices/ApplicationServices.h>
+#include <time.h>
+
+#include "base/mac/mac_util.h"
+#include "base/memory/ref_counted.h"
+#include "skia/ext/bitmap_platform_device_data.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/core/SkTypes.h"
+#include "third_party/skia/include/core/SkUtils.h"
+
+namespace skia {
+
+namespace {
+
+static CGContextRef CGContextForData(void* data, int width, int height) {
+#define HAS_ARGB_SHIFTS(a, r, g, b) \
+ (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
+ && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
+#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
+ // Allocate a bitmap context with 4 components per pixel (BGRA). Apple
+ // recommends these flags for improved CG performance.
+
+ // CGBitmapContextCreate returns NULL if width/height are 0. However, our
+ // callers expect to get a canvas back (which they later resize/reallocate)
+ // so we pin the dimensions here.
+ width = SkMax32(1, width);
+ height = SkMax32(1, height);
+ CGContextRef context =
+ CGBitmapContextCreate(data, width, height, 8, width * 4,
+ base::mac::GetSystemColorSpace(),
+ kCGImageAlphaPremultipliedFirst |
+ kCGBitmapByteOrder32Host);
+#else
+#error We require that Skia's and CoreGraphics's recommended \
+ image memory layout match.
+#endif
+#undef HAS_ARGB_SHIFTS
+
+ if (!context)
+ return NULL;
+
+ // Change the coordinate system to match WebCore's
+ CGContextTranslateCTM(context, 0, height);
+ CGContextScaleCTM(context, 1.0, -1.0);
+
+ return context;
+}
+
+} // namespace
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
+ CGContextRef bitmap)
+ : bitmap_context_(bitmap),
+ config_dirty_(true), // Want to load the config next time.
+ transform_(SkMatrix::I()) {
+ SkASSERT(bitmap_context_);
+ // Initialize the clip region to the entire bitmap.
+
+ SkIRect rect;
+ rect.set(0, 0,
+ CGBitmapContextGetWidth(bitmap_context_),
+ CGBitmapContextGetHeight(bitmap_context_));
+ clip_region_ = SkRegion(rect);
+ CGContextRetain(bitmap_context_);
+ // We must save the state once so that we can use the restore/save trick
+ // in LoadConfig().
+ CGContextSaveGState(bitmap_context_);
+}
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
+ if (bitmap_context_)
+ CGContextRelease(bitmap_context_);
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapContext() {
+ SkASSERT(bitmap_context_);
+ CGContextRelease(bitmap_context_);
+ bitmap_context_ = NULL;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
+ const SkMatrix& transform,
+ const SkRegion& region) {
+ transform_ = transform;
+ clip_region_ = region;
+ config_dirty_ = true;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
+ if (!config_dirty_ || !bitmap_context_)
+ return; // Nothing to do.
+ config_dirty_ = false;
+
+ // We must restore and then save the state of the graphics context since the
+ // calls to Load the clipping region to the context are strictly cummulative,
+ // i.e., you can't replace a clip rect, other than with a save/restore.
+ // But this implies that no other changes to the state are done elsewhere.
+ // If we ever get to need to change this, then we must replace the clip rect
+ // calls in LoadClippingRegionToCGContext() with an image mask instead.
+ CGContextRestoreGState(bitmap_context_);
+ CGContextSaveGState(bitmap_context_);
+ LoadTransformToCGContext(bitmap_context_, transform_);
+ LoadClippingRegionToCGContext(bitmap_context_, clip_region_, transform_);
+}
+
+
+// We use this static factory function instead of the regular constructor so
+// that we can create the pixel data before calling the constructor. This is
+// required so that we can call the base class' constructor with the pixel
+// data.
+BitmapPlatformDevice* BitmapPlatformDevice::Create(CGContextRef context,
+ int width,
+ int height,
+ bool is_opaque) {
+ if (RasterDeviceTooBigToAllocate(width, height))
+ return NULL;
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ if (bitmap.allocPixels() != true)
+ return NULL;
+
+ void* data = NULL;
+ if (context) {
+ data = CGBitmapContextGetData(context);
+ bitmap.setPixels(data);
+ } else {
+ data = bitmap.getPixels();
+ }
+
+ bitmap.setIsOpaque(is_opaque);
+
+ // If we were given data, then don't clobber it!
+#ifndef NDEBUG
+ if (!context && is_opaque) {
+ // To aid in finding bugs, we set the background color to something
+ // obviously wrong so it will be noticable when it is not cleared
+ bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green
+ }
+#endif
+
+ if (!context) {
+ context = CGContextForData(data, width, height);
+ if (!context)
+ return NULL;
+ } else
+ CGContextRetain(context);
+
+ BitmapPlatformDevice* rv = new BitmapPlatformDevice(
+ skia::AdoptRef(new BitmapPlatformDeviceData(context)), bitmap);
+
+ // The device object took ownership of the graphics context with its own
+ // CGContextRetain call.
+ CGContextRelease(context);
+
+ return rv;
+}
+
+BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
+ int height,
+ bool is_opaque) {
+ BitmapPlatformDevice* device = Create(NULL, width, height, is_opaque);
+ if (!is_opaque)
+ device->accessBitmap(true).eraseARGB(0, 0, 0, 0);
+ return device;
+}
+
+BitmapPlatformDevice* BitmapPlatformDevice::CreateWithData(uint8_t* data,
+ int width,
+ int height,
+ bool is_opaque) {
+ CGContextRef context = NULL;
+ if (data)
+ context = CGContextForData(data, width, height);
+
+ BitmapPlatformDevice* rv = Create(context, width, height, is_opaque);
+
+ // The device object took ownership of the graphics context with its own
+ // CGContextRetain call.
+ if (context)
+ CGContextRelease(context);
+
+ return rv;
+}
+
+// The device will own the bitmap, which corresponds to also owning the pixel
+// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
+BitmapPlatformDevice::BitmapPlatformDevice(
+ const skia::RefPtr<BitmapPlatformDeviceData>& data, const SkBitmap& bitmap)
+ : SkDevice(bitmap),
+ data_(data) {
+ SetPlatformDevice(this, this);
+}
+
+BitmapPlatformDevice::~BitmapPlatformDevice() {
+}
+
+CGContextRef BitmapPlatformDevice::GetBitmapContext() {
+ data_->LoadConfig();
+ return data_->bitmap_context();
+}
+
+void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
+ const SkRegion& region,
+ const SkClipStack&) {
+ data_->SetMatrixClip(transform, region);
+}
+
+void BitmapPlatformDevice::DrawToNativeContext(CGContextRef context, int x,
+ int y, const CGRect* src_rect) {
+ bool created_dc = false;
+ if (!data_->bitmap_context()) {
+ created_dc = true;
+ GetBitmapContext();
+ }
+
+ // this should not make a copy of the bits, since we're not doing
+ // anything to trigger copy on write
+ CGImageRef image = CGBitmapContextCreateImage(data_->bitmap_context());
+ CGRect bounds;
+ bounds.origin.x = x;
+ bounds.origin.y = y;
+ if (src_rect) {
+ bounds.size.width = src_rect->size.width;
+ bounds.size.height = src_rect->size.height;
+ CGImageRef sub_image = CGImageCreateWithImageInRect(image, *src_rect);
+ CGContextDrawImage(context, bounds, sub_image);
+ CGImageRelease(sub_image);
+ } else {
+ bounds.size.width = width();
+ bounds.size.height = height();
+ CGContextDrawImage(context, bounds, image);
+ }
+ CGImageRelease(image);
+
+ if (created_dc)
+ data_->ReleaseBitmapContext();
+}
+
+const SkBitmap& BitmapPlatformDevice::onAccessBitmap(SkBitmap* bitmap) {
+ // Not needed in CoreGraphics
+ return *bitmap;
+}
+
+SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
+ SkBitmap::Config config, int width, int height, bool isOpaque,
+ Usage /*usage*/) {
+ SkASSERT(config == SkBitmap::kARGB_8888_Config);
+ SkDevice* bitmap_device = BitmapPlatformDevice::CreateAndClear(width, height,
+ isOpaque);
+ return bitmap_device;
+}
+
+// PlatformCanvas impl
+
+SkCanvas* CreatePlatformCanvas(CGContextRef ctx, int width, int height,
+ bool is_opaque, OnFailureType failureType) {
+ skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+ BitmapPlatformDevice::Create(ctx, width, height, is_opaque));
+ return CreateCanvas(dev, failureType);
+}
+
+SkCanvas* CreatePlatformCanvas(int width, int height, bool is_opaque,
+ uint8_t* data, OnFailureType failureType) {
+ skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+ BitmapPlatformDevice::CreateWithData(data, width, height, is_opaque));
+ return CreateCanvas(dev, failureType);
+}
+
+// Port of PlatformBitmap to mac
+
+PlatformBitmap::~PlatformBitmap() {
+ if (surface_)
+ CGContextRelease(surface_);
+}
+
+bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
+ if (RasterDeviceTooBigToAllocate(width, height))
+ return false;
+
+ bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height, width * 4);
+ if (!bitmap_.allocPixels())
+ return false;
+
+ if (!is_opaque)
+ bitmap_.eraseColor(0);
+ bitmap_.setIsOpaque(is_opaque);
+
+ surface_ = CGContextForData(bitmap_.getPixels(), bitmap_.width(),
+ bitmap_.height());
+ return true;
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/bitmap_platform_device_mac.h b/chromium/skia/ext/bitmap_platform_device_mac.h
new file mode 100644
index 00000000000..a1c5894086e
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_mac.h
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_MAC_H_
+#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_MAC_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "skia/ext/platform_device.h"
+#include "skia/ext/refptr.h"
+
+namespace skia {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. Our device provides a surface CoreGraphics can also
+// write to. BitmapPlatformDevice creates a bitmap using
+// CGCreateBitmapContext() in a format that Skia supports and can then use this
+// to draw text into, etc. This pixel data is provided to the bitmap that the
+// device contains so that it can be shared.
+//
+// The device owns the pixel data, when the device goes away, the pixel data
+// also becomes invalid. THIS IS DIFFERENT THAN NORMAL SKIA which uses
+// reference counting for the pixel data. In normal Skia, you could assign
+// another bitmap to this device's bitmap and everything will work properly.
+// For us, that other bitmap will become invalid as soon as the device becomes
+// invalid, which may lead to subtle bugs. Therefore, DO NOT ASSIGN THE
+// DEVICE'S PIXEL DATA TO ANOTHER BITMAP, make sure you copy instead.
+class SK_API BitmapPlatformDevice : public SkDevice, public PlatformDevice {
+ public:
+ // Creates a BitmapPlatformDevice instance. |is_opaque| should be set if the
+ // caller knows the bitmap will be completely opaque and allows some
+ // optimizations.
+ // |context| may be NULL. If |context| is NULL, then the bitmap backing store
+ // is not initialized.
+ static BitmapPlatformDevice* Create(CGContextRef context,
+ int width, int height,
+ bool is_opaque);
+
+ // Creates a BitmapPlatformDevice instance. If |is_opaque| is false,
+ // then the bitmap is initialzed to 0.
+ static BitmapPlatformDevice* CreateAndClear(int width, int height,
+ bool is_opaque);
+
+ // Creates a context for |data| and calls Create.
+ // If |data| is NULL, then the bitmap backing store is not initialized.
+ static BitmapPlatformDevice* CreateWithData(uint8_t* data,
+ int width, int height,
+ bool is_opaque);
+
+ virtual ~BitmapPlatformDevice();
+
+ // PlatformDevice overrides
+ virtual CGContextRef GetBitmapContext() OVERRIDE;
+ virtual void DrawToNativeContext(CGContextRef context, int x, int y,
+ const CGRect* src_rect) OVERRIDE;
+
+ // SkDevice overrides
+ virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
+ const SkClipStack&) OVERRIDE;
+
+ protected:
+ // Reference counted data that can be shared between multiple devices. This
+ // allows copy constructors and operator= for devices to work properly. The
+ // bitmaps used by the base device class are already refcounted and copyable.
+ class BitmapPlatformDeviceData;
+
+ BitmapPlatformDevice(const skia::RefPtr<BitmapPlatformDeviceData>& data,
+ const SkBitmap& bitmap);
+
+ // Flushes the CoreGraphics context so that the pixel data can be accessed
+ // directly by Skia. Overridden from SkDevice, this is called when Skia
+ // starts accessing pixel data.
+ virtual const SkBitmap& onAccessBitmap(SkBitmap*) OVERRIDE;
+
+ virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+ int height, bool isOpaque,
+ Usage usage) OVERRIDE;
+
+ // Data associated with this device, guaranteed non-null.
+ skia::RefPtr<BitmapPlatformDeviceData> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_BITMAP_PLATFORM_DEVICE_MAC_H_
diff --git a/chromium/skia/ext/bitmap_platform_device_mac_unittest.cc b/chromium/skia/ext/bitmap_platform_device_mac_unittest.cc
new file mode 100644
index 00000000000..7265bc4cfb3
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_mac_unittest.cc
@@ -0,0 +1,69 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/bitmap_platform_device_mac.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "skia/ext/skia_utils_mac.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/core/SkClipStack.h"
+
+namespace skia {
+
+const int kWidth = 400;
+const int kHeight = 300;
+
+class BitmapPlatformDeviceMacTest : public testing::Test {
+ public:
+ BitmapPlatformDeviceMacTest() {
+ bitmap_.reset(BitmapPlatformDevice::Create(
+ NULL, kWidth, kHeight, /*is_opaque=*/true));
+ }
+
+ scoped_ptr<BitmapPlatformDevice> bitmap_;
+};
+
+TEST_F(BitmapPlatformDeviceMacTest, ClipRectTransformWithTranslate) {
+ SkMatrix transform;
+ transform.setTranslate(50, 140);
+
+ SkClipStack ignore;
+ SkRegion clip_region;
+ SkIRect rect;
+ rect.set(0, 0, kWidth, kHeight);
+ clip_region.setRect(rect);
+ bitmap_->setMatrixClip(transform, clip_region, ignore);
+
+ CGContextRef context = bitmap_->GetBitmapContext();
+ SkRect clip_rect = gfx::CGRectToSkRect(CGContextGetClipBoundingBox(context));
+ transform.mapRect(&clip_rect);
+ EXPECT_EQ(0, clip_rect.fLeft);
+ EXPECT_EQ(0, clip_rect.fTop);
+ EXPECT_EQ(kWidth, clip_rect.width());
+ EXPECT_EQ(kHeight, clip_rect.height());
+}
+
+TEST_F(BitmapPlatformDeviceMacTest, ClipRectTransformWithScale) {
+ SkMatrix transform;
+ transform.setScale(0.5, 0.5);
+
+ SkClipStack unused;
+ SkRegion clip_region;
+ SkIRect rect;
+ rect.set(0, 0, kWidth, kHeight);
+ clip_region.setRect(rect);
+ bitmap_->setMatrixClip(transform, clip_region, unused);
+
+ CGContextRef context = bitmap_->GetBitmapContext();
+ SkRect clip_rect = gfx::CGRectToSkRect(CGContextGetClipBoundingBox(context));
+ transform.mapRect(&clip_rect);
+ EXPECT_EQ(0, clip_rect.fLeft);
+ EXPECT_EQ(0, clip_rect.fTop);
+ EXPECT_EQ(kWidth, clip_rect.width());
+ EXPECT_EQ(kHeight, clip_rect.height());
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/bitmap_platform_device_win.cc b/chromium/skia/ext/bitmap_platform_device_win.cc
new file mode 100644
index 00000000000..201866aa17e
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_win.cc
@@ -0,0 +1,356 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <windows.h>
+#include <psapi.h>
+
+#include "skia/ext/bitmap_platform_device_win.h"
+#include "skia/ext/bitmap_platform_device_data.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/core/SkUtils.h"
+
+namespace {
+
+// PlatformBitmapPixelRef is an SkPixelRef that, on Windows, is backed by an
+// HBITMAP.
+class SK_API PlatformBitmapPixelRef : public SkPixelRef {
+ public:
+ PlatformBitmapPixelRef(HBITMAP bitmap_handle, void* pixels);
+ virtual ~PlatformBitmapPixelRef();
+
+ SK_DECLARE_UNFLATTENABLE_OBJECT();
+
+ protected:
+ virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
+ virtual void onUnlockPixels() SK_OVERRIDE;
+
+ private:
+ HBITMAP bitmap_handle_;
+ void* pixels_;
+};
+
+HBITMAP CreateHBitmap(int width, int height, bool is_opaque,
+ HANDLE shared_section, void** data) {
+ // CreateDIBSection appears to get unhappy if we create an empty bitmap, so
+ // just create a minimal bitmap
+ if ((width == 0) || (height == 0)) {
+ width = 1;
+ height = 1;
+ }
+
+ BITMAPINFOHEADER hdr = {0};
+ hdr.biSize = sizeof(BITMAPINFOHEADER);
+ hdr.biWidth = width;
+ hdr.biHeight = -height; // minus means top-down bitmap
+ hdr.biPlanes = 1;
+ hdr.biBitCount = 32;
+ hdr.biCompression = BI_RGB; // no compression
+ hdr.biSizeImage = 0;
+ hdr.biXPelsPerMeter = 1;
+ hdr.biYPelsPerMeter = 1;
+ hdr.biClrUsed = 0;
+ hdr.biClrImportant = 0;
+
+ HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
+ 0, data, shared_section, 0);
+ return hbitmap;
+}
+
+PlatformBitmapPixelRef::PlatformBitmapPixelRef(HBITMAP bitmap_handle,
+ void* pixels)
+ : bitmap_handle_(bitmap_handle),
+ pixels_(pixels) {
+ setPreLocked(pixels, NULL);
+}
+
+PlatformBitmapPixelRef::~PlatformBitmapPixelRef() {
+ if (bitmap_handle_)
+ DeleteObject(bitmap_handle_);
+}
+
+void* PlatformBitmapPixelRef::onLockPixels(SkColorTable** color_table) {
+ *color_table = NULL;
+ return pixels_;
+}
+
+void PlatformBitmapPixelRef::onUnlockPixels() {
+ // Nothing to do.
+ return;
+}
+
+} // namespace
+
+namespace skia {
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::BitmapPlatformDeviceData(
+ HBITMAP hbitmap)
+ : bitmap_context_(hbitmap),
+ hdc_(NULL),
+ config_dirty_(true), // Want to load the config next time.
+ transform_(SkMatrix::I()) {
+ // Initialize the clip region to the entire bitmap.
+ BITMAP bitmap_data;
+ if (GetObject(bitmap_context_, sizeof(BITMAP), &bitmap_data)) {
+ SkIRect rect;
+ rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight);
+ clip_region_ = SkRegion(rect);
+ }
+}
+
+BitmapPlatformDevice::BitmapPlatformDeviceData::~BitmapPlatformDeviceData() {
+ if (hdc_)
+ ReleaseBitmapDC();
+
+ // this will free the bitmap data as well as the bitmap handle
+ DeleteObject(bitmap_context_);
+}
+
+HDC BitmapPlatformDevice::BitmapPlatformDeviceData::GetBitmapDC() {
+ if (!hdc_) {
+ hdc_ = CreateCompatibleDC(NULL);
+ InitializeDC(hdc_);
+ HGDIOBJ old_bitmap = SelectObject(hdc_, bitmap_context_);
+ // When the memory DC is created, its display surface is exactly one
+ // monochrome pixel wide and one monochrome pixel high. Since we select our
+ // own bitmap, we must delete the previous one.
+ DeleteObject(old_bitmap);
+ }
+
+ LoadConfig();
+ return hdc_;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::ReleaseBitmapDC() {
+ SkASSERT(hdc_);
+ DeleteDC(hdc_);
+ hdc_ = NULL;
+}
+
+bool BitmapPlatformDevice::BitmapPlatformDeviceData::IsBitmapDCCreated()
+ const {
+ return hdc_ != NULL;
+}
+
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::SetMatrixClip(
+ const SkMatrix& transform,
+ const SkRegion& region) {
+ transform_ = transform;
+ clip_region_ = region;
+ config_dirty_ = true;
+}
+
+void BitmapPlatformDevice::BitmapPlatformDeviceData::LoadConfig() {
+ if (!config_dirty_ || !hdc_)
+ return; // Nothing to do.
+ config_dirty_ = false;
+
+ // Transform.
+ LoadTransformToDC(hdc_, transform_);
+ LoadClippingRegionToDC(hdc_, clip_region_, transform_);
+}
+
+// We use this static factory function instead of the regular constructor so
+// that we can create the pixel data before calling the constructor. This is
+// required so that we can call the base class' constructor with the pixel
+// data.
+BitmapPlatformDevice* BitmapPlatformDevice::Create(
+ int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section) {
+
+ void* data;
+ HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, shared_section,
+ &data);
+ if (!hbitmap)
+ return NULL;
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap.setPixels(data);
+ bitmap.setIsOpaque(is_opaque);
+
+#ifndef NDEBUG
+ // If we were given data, then don't clobber it!
+ if (!shared_section && is_opaque)
+ // To aid in finding bugs, we set the background color to something
+ // obviously wrong so it will be noticable when it is not cleared
+ bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green
+#endif
+
+ // The device object will take ownership of the HBITMAP. The initial refcount
+ // of the data object will be 1, which is what the constructor expects.
+ return new BitmapPlatformDevice(
+ skia::AdoptRef(new BitmapPlatformDeviceData(hbitmap)), bitmap);
+}
+
+// static
+BitmapPlatformDevice* BitmapPlatformDevice::Create(int width, int height,
+ bool is_opaque) {
+ return Create(width, height, is_opaque, NULL);
+}
+
+// static
+BitmapPlatformDevice* BitmapPlatformDevice::CreateAndClear(int width,
+ int height,
+ bool is_opaque) {
+ BitmapPlatformDevice* device = BitmapPlatformDevice::Create(width, height,
+ is_opaque);
+ if (device && !is_opaque)
+ device->accessBitmap(true).eraseARGB(0, 0, 0, 0);
+ return device;
+}
+
+// The device will own the HBITMAP, which corresponds to also owning the pixel
+// data. Therefore, we do not transfer ownership to the SkDevice's bitmap.
+BitmapPlatformDevice::BitmapPlatformDevice(
+ const skia::RefPtr<BitmapPlatformDeviceData>& data,
+ const SkBitmap& bitmap)
+ : SkDevice(bitmap),
+ data_(data) {
+ // The data object is already ref'ed for us by create().
+ SkDEBUGCODE(begin_paint_count_ = 0);
+ SetPlatformDevice(this, this);
+}
+
+BitmapPlatformDevice::~BitmapPlatformDevice() {
+ SkASSERT(begin_paint_count_ == 0);
+}
+
+HDC BitmapPlatformDevice::BeginPlatformPaint() {
+ SkDEBUGCODE(begin_paint_count_++);
+ return data_->GetBitmapDC();
+}
+
+void BitmapPlatformDevice::EndPlatformPaint() {
+ SkASSERT(begin_paint_count_--);
+ PlatformDevice::EndPlatformPaint();
+}
+
+void BitmapPlatformDevice::setMatrixClip(const SkMatrix& transform,
+ const SkRegion& region,
+ const SkClipStack&) {
+ data_->SetMatrixClip(transform, region);
+}
+
+void BitmapPlatformDevice::DrawToNativeContext(HDC dc, int x, int y,
+ const RECT* src_rect) {
+ bool created_dc = !data_->IsBitmapDCCreated();
+ HDC source_dc = BeginPlatformPaint();
+
+ RECT temp_rect;
+ if (!src_rect) {
+ temp_rect.left = 0;
+ temp_rect.right = width();
+ temp_rect.top = 0;
+ temp_rect.bottom = height();
+ src_rect = &temp_rect;
+ }
+
+ int copy_width = src_rect->right - src_rect->left;
+ int copy_height = src_rect->bottom - src_rect->top;
+
+ // We need to reset the translation for our bitmap or (0,0) won't be in the
+ // upper left anymore
+ SkMatrix identity;
+ identity.reset();
+
+ LoadTransformToDC(source_dc, identity);
+ if (isOpaque()) {
+ BitBlt(dc,
+ x,
+ y,
+ copy_width,
+ copy_height,
+ source_dc,
+ src_rect->left,
+ src_rect->top,
+ SRCCOPY);
+ } else {
+ SkASSERT(copy_width != 0 && copy_height != 0);
+ BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA};
+ GdiAlphaBlend(dc,
+ x,
+ y,
+ copy_width,
+ copy_height,
+ source_dc,
+ src_rect->left,
+ src_rect->top,
+ copy_width,
+ copy_height,
+ blend_function);
+ }
+ LoadTransformToDC(source_dc, data_->transform());
+
+ EndPlatformPaint();
+ if (created_dc)
+ data_->ReleaseBitmapDC();
+}
+
+const SkBitmap& BitmapPlatformDevice::onAccessBitmap(SkBitmap* bitmap) {
+ // FIXME(brettw) OPTIMIZATION: We should only flush if we know a GDI
+ // operation has occurred on our DC.
+ if (data_->IsBitmapDCCreated())
+ GdiFlush();
+ return *bitmap;
+}
+
+SkDevice* BitmapPlatformDevice::onCreateCompatibleDevice(
+ SkBitmap::Config config, int width, int height, bool isOpaque, Usage) {
+ SkASSERT(config == SkBitmap::kARGB_8888_Config);
+ return BitmapPlatformDevice::CreateAndClear(width, height, isOpaque);
+}
+
+// PlatformCanvas impl
+
+SkCanvas* CreatePlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section,
+ OnFailureType failureType) {
+ skia::RefPtr<SkDevice> dev = skia::AdoptRef(
+ BitmapPlatformDevice::Create(width, height, is_opaque, shared_section));
+ return CreateCanvas(dev, failureType);
+}
+
+// Port of PlatformBitmap to win
+
+PlatformBitmap::~PlatformBitmap() {
+ if (surface_) {
+ if (platform_extra_)
+ SelectObject(surface_, reinterpret_cast<HGDIOBJ>(platform_extra_));
+ DeleteDC(surface_);
+ }
+}
+
+bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
+ void* data;
+ HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, 0, &data);
+ if (!hbitmap)
+ return false;
+
+ surface_ = CreateCompatibleDC(NULL);
+ InitializeDC(surface_);
+ // When the memory DC is created, its display surface is exactly one
+ // monochrome pixel wide and one monochrome pixel high. Save this object
+ // off, we'll restore it just before deleting the memory DC.
+ HGDIOBJ stock_bitmap = SelectObject(surface_, hbitmap);
+ platform_extra_ = reinterpret_cast<intptr_t>(stock_bitmap);
+
+ bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ // PlatformBitmapPixelRef takes ownership of |hbitmap|.
+ bitmap_.setPixelRef(
+ skia::AdoptRef(new PlatformBitmapPixelRef(hbitmap, data)).get());
+ bitmap_.setIsOpaque(is_opaque);
+ bitmap_.lockPixels();
+
+ return true;
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/bitmap_platform_device_win.h b/chromium/skia/ext/bitmap_platform_device_win.h
new file mode 100644
index 00000000000..c896c0aaae7
--- /dev/null
+++ b/chromium/skia/ext/bitmap_platform_device_win.h
@@ -0,0 +1,99 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_BITMAP_PLATFORM_DEVICE_WIN_H_
+#define SKIA_EXT_BITMAP_PLATFORM_DEVICE_WIN_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "skia/ext/platform_device.h"
+#include "skia/ext/refptr.h"
+
+namespace skia {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. Our device provides a surface Windows can also write
+// to. BitmapPlatformDevice creates a bitmap using CreateDIBSection() in a
+// format that Skia supports and can then use this to draw ClearType into, etc.
+// This pixel data is provided to the bitmap that the device contains so that it
+// can be shared.
+//
+// The device owns the pixel data, when the device goes away, the pixel data
+// also becomes invalid. THIS IS DIFFERENT THAN NORMAL SKIA which uses
+// reference counting for the pixel data. In normal Skia, you could assign
+// another bitmap to this device's bitmap and everything will work properly.
+// For us, that other bitmap will become invalid as soon as the device becomes
+// invalid, which may lead to subtle bugs. Therefore, DO NOT ASSIGN THE
+// DEVICE'S PIXEL DATA TO ANOTHER BITMAP, make sure you copy instead.
+class SK_API BitmapPlatformDevice : public SkDevice, public PlatformDevice {
+ public:
+ // Factory function. is_opaque should be set if the caller knows the bitmap
+ // will be completely opaque and allows some optimizations.
+ //
+ // The |shared_section| parameter is optional (pass NULL for default
+ // behavior). If |shared_section| is non-null, then it must be a handle to a
+ // file-mapping object returned by CreateFileMapping. See CreateDIBSection
+ // for details. If |shared_section| is null, the bitmap backing store is not
+ // initialized.
+ static BitmapPlatformDevice* Create(int width, int height,
+ bool is_opaque, HANDLE shared_section);
+
+ // Create a BitmapPlatformDevice with no shared section. The bitmap is not
+ // initialized to 0.
+ static BitmapPlatformDevice* Create(int width, int height, bool is_opaque);
+
+ // Creates a BitmapPlatformDevice instance respecting the parameters as above.
+ // If |is_opaque| is false, then the bitmap is initialzed to 0.
+ static BitmapPlatformDevice* CreateAndClear(int width, int height,
+ bool is_opaque);
+
+ virtual ~BitmapPlatformDevice();
+
+ // PlatformDevice overrides
+ // Retrieves the bitmap DC, which is the memory DC for our bitmap data. The
+ // bitmap DC is lazy created.
+ virtual PlatformSurface BeginPlatformPaint() OVERRIDE;
+ virtual void EndPlatformPaint() OVERRIDE;
+
+ virtual void DrawToNativeContext(HDC dc, int x, int y,
+ const RECT* src_rect) OVERRIDE;
+
+ // Loads the given transform and clipping region into the HDC. This is
+ // overridden from SkDevice.
+ virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
+ const SkClipStack&) OVERRIDE;
+
+ protected:
+ // Flushes the Windows device context so that the pixel data can be accessed
+ // directly by Skia. Overridden from SkDevice, this is called when Skia
+ // starts accessing pixel data.
+ virtual const SkBitmap& onAccessBitmap(SkBitmap* bitmap) OVERRIDE;
+
+ virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+ int height, bool isOpaque,
+ Usage usage) OVERRIDE;
+
+ private:
+ // Reference counted data that can be shared between multiple devices. This
+ // allows copy constructors and operator= for devices to work properly. The
+ // bitmaps used by the base device class are already refcounted and copyable.
+ class BitmapPlatformDeviceData;
+
+ // Private constructor.
+ BitmapPlatformDevice(const skia::RefPtr<BitmapPlatformDeviceData>& data,
+ const SkBitmap& bitmap);
+
+ // Data associated with this device, guaranteed non-null.
+ skia::RefPtr<BitmapPlatformDeviceData> data_;
+
+#ifdef SK_DEBUG
+ int begin_paint_count_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_BITMAP_PLATFORM_DEVICE_WIN_H_
diff --git a/chromium/skia/ext/convolver.cc b/chromium/skia/ext/convolver.cc
new file mode 100644
index 00000000000..4b40ffd2cea
--- /dev/null
+++ b/chromium/skia/ext/convolver.cc
@@ -0,0 +1,716 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "skia/ext/convolver.h"
+#include "skia/ext/convolver_SSE2.h"
+#include "skia/ext/convolver_mips_dspr2.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+namespace skia {
+
+namespace {
+
+// Converts the argument to an 8-bit unsigned value by clamping to the range
+// 0-255.
+inline unsigned char ClampTo8(int a) {
+ if (static_cast<unsigned>(a) < 256)
+ return a; // Avoid the extra check in the common case.
+ if (a < 0)
+ return 0;
+ return 255;
+}
+
+// Takes the value produced by accumulating element-wise product of image with
+// a kernel and brings it back into range.
+// All of the filter scaling factors are in fixed point with kShiftBits bits of
+// fractional part.
+inline unsigned char BringBackTo8(int a, bool take_absolute) {
+ a >>= ConvolutionFilter1D::kShiftBits;
+ if (take_absolute)
+ a = std::abs(a);
+ return ClampTo8(a);
+}
+
+// Stores a list of rows in a circular buffer. The usage is you write into it
+// by calling AdvanceRow. It will keep track of which row in the buffer it
+// should use next, and the total number of rows added.
+class CircularRowBuffer {
+ public:
+ // The number of pixels in each row is given in |source_row_pixel_width|.
+ // The maximum number of rows needed in the buffer is |max_y_filter_size|
+ // (we only need to store enough rows for the biggest filter).
+ //
+ // We use the |first_input_row| to compute the coordinates of all of the
+ // following rows returned by Advance().
+ CircularRowBuffer(int dest_row_pixel_width, int max_y_filter_size,
+ int first_input_row)
+ : row_byte_width_(dest_row_pixel_width * 4),
+ num_rows_(max_y_filter_size),
+ next_row_(0),
+ next_row_coordinate_(first_input_row) {
+ buffer_.resize(row_byte_width_ * max_y_filter_size);
+ row_addresses_.resize(num_rows_);
+ }
+
+ // Moves to the next row in the buffer, returning a pointer to the beginning
+ // of it.
+ unsigned char* AdvanceRow() {
+ unsigned char* row = &buffer_[next_row_ * row_byte_width_];
+ next_row_coordinate_++;
+
+ // Set the pointer to the next row to use, wrapping around if necessary.
+ next_row_++;
+ if (next_row_ == num_rows_)
+ next_row_ = 0;
+ return row;
+ }
+
+ // Returns a pointer to an "unrolled" array of rows. These rows will start
+ // at the y coordinate placed into |*first_row_index| and will continue in
+ // order for the maximum number of rows in this circular buffer.
+ //
+ // The |first_row_index_| may be negative. This means the circular buffer
+ // starts before the top of the image (it hasn't been filled yet).
+ unsigned char* const* GetRowAddresses(int* first_row_index) {
+ // Example for a 4-element circular buffer holding coords 6-9.
+ // Row 0 Coord 8
+ // Row 1 Coord 9
+ // Row 2 Coord 6 <- next_row_ = 2, next_row_coordinate_ = 10.
+ // Row 3 Coord 7
+ //
+ // The "next" row is also the first (lowest) coordinate. This computation
+ // may yield a negative value, but that's OK, the math will work out
+ // since the user of this buffer will compute the offset relative
+ // to the first_row_index and the negative rows will never be used.
+ *first_row_index = next_row_coordinate_ - num_rows_;
+
+ int cur_row = next_row_;
+ for (int i = 0; i < num_rows_; i++) {
+ row_addresses_[i] = &buffer_[cur_row * row_byte_width_];
+
+ // Advance to the next row, wrapping if necessary.
+ cur_row++;
+ if (cur_row == num_rows_)
+ cur_row = 0;
+ }
+ return &row_addresses_[0];
+ }
+
+ private:
+ // The buffer storing the rows. They are packed, each one row_byte_width_.
+ std::vector<unsigned char> buffer_;
+
+ // Number of bytes per row in the |buffer_|.
+ int row_byte_width_;
+
+ // The number of rows available in the buffer.
+ int num_rows_;
+
+ // The next row index we should write into. This wraps around as the
+ // circular buffer is used.
+ int next_row_;
+
+ // The y coordinate of the |next_row_|. This is incremented each time a
+ // new row is appended and does not wrap.
+ int next_row_coordinate_;
+
+ // Buffer used by GetRowAddresses().
+ std::vector<unsigned char*> row_addresses_;
+};
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+template<bool has_alpha>
+void ConvolveHorizontally(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row) {
+ // Loop over each pixel on this row in the output image.
+ int num_values = filter.num_values();
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ // Get the filter that determines the current output pixel.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const unsigned char* row_to_filter = &src_data[filter_offset * 4];
+
+ // Apply the filter to the row to get the destination pixel in |accum|.
+ int accum[4] = {0};
+ for (int filter_x = 0; filter_x < filter_length; filter_x++) {
+ ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_x];
+ accum[0] += cur_filter * row_to_filter[filter_x * 4 + 0];
+ accum[1] += cur_filter * row_to_filter[filter_x * 4 + 1];
+ accum[2] += cur_filter * row_to_filter[filter_x * 4 + 2];
+ if (has_alpha)
+ accum[3] += cur_filter * row_to_filter[filter_x * 4 + 3];
+ }
+
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of fractional part.
+ accum[0] >>= ConvolutionFilter1D::kShiftBits;
+ accum[1] >>= ConvolutionFilter1D::kShiftBits;
+ accum[2] >>= ConvolutionFilter1D::kShiftBits;
+ if (has_alpha)
+ accum[3] >>= ConvolutionFilter1D::kShiftBits;
+
+ // Store the new pixel.
+ out_row[out_x * 4 + 0] = ClampTo8(accum[0]);
+ out_row[out_x * 4 + 1] = ClampTo8(accum[1]);
+ out_row[out_x * 4 + 2] = ClampTo8(accum[2]);
+ if (has_alpha)
+ out_row[out_x * 4 + 3] = ClampTo8(accum[3]);
+ }
+}
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row) {
+ // We go through each column in the output and do a vertical convolution,
+ // generating one output pixel each time.
+ for (int out_x = 0; out_x < pixel_width; out_x++) {
+ // Compute the number of bytes over in each row that the current column
+ // we're convolving starts at. The pixel will cover the next 4 bytes.
+ int byte_offset = out_x * 4;
+
+ // Apply the filter to one column of pixels.
+ int accum[4] = {0};
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+ ConvolutionFilter1D::Fixed cur_filter = filter_values[filter_y];
+ accum[0] += cur_filter * source_data_rows[filter_y][byte_offset + 0];
+ accum[1] += cur_filter * source_data_rows[filter_y][byte_offset + 1];
+ accum[2] += cur_filter * source_data_rows[filter_y][byte_offset + 2];
+ if (has_alpha)
+ accum[3] += cur_filter * source_data_rows[filter_y][byte_offset + 3];
+ }
+
+ // Bring this value back in range. All of the filter scaling factors
+ // are in fixed point with kShiftBits bits of precision.
+ accum[0] >>= ConvolutionFilter1D::kShiftBits;
+ accum[1] >>= ConvolutionFilter1D::kShiftBits;
+ accum[2] >>= ConvolutionFilter1D::kShiftBits;
+ if (has_alpha)
+ accum[3] >>= ConvolutionFilter1D::kShiftBits;
+
+ // Store the new pixel.
+ out_row[byte_offset + 0] = ClampTo8(accum[0]);
+ out_row[byte_offset + 1] = ClampTo8(accum[1]);
+ out_row[byte_offset + 2] = ClampTo8(accum[2]);
+ if (has_alpha) {
+ unsigned char alpha = ClampTo8(accum[3]);
+
+ // Make sure the alpha channel doesn't come out smaller than any of the
+ // color channels. We use premultipled alpha channels, so this should
+ // never happen, but rounding errors will cause this from time to time.
+ // These "impossible" colors will cause overflows (and hence random pixel
+ // values) when the resulting bitmap is drawn to the screen.
+ //
+ // We only need to do this when generating the final output row (here).
+ int max_color_channel = std::max(out_row[byte_offset + 0],
+ std::max(out_row[byte_offset + 1], out_row[byte_offset + 2]));
+ if (alpha < max_color_channel)
+ out_row[byte_offset + 3] = max_color_channel;
+ else
+ out_row[byte_offset + 3] = alpha;
+ } else {
+ // No alpha channel, the image is opaque.
+ out_row[byte_offset + 3] = 0xff;
+ }
+ }
+}
+
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row,
+ bool source_has_alpha) {
+ if (source_has_alpha) {
+ ConvolveVertically<true>(filter_values, filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row);
+ } else {
+ ConvolveVertically<false>(filter_values, filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row);
+ }
+}
+
+} // namespace
+
+// ConvolutionFilter1D ---------------------------------------------------------
+
+ConvolutionFilter1D::ConvolutionFilter1D()
+ : max_filter_(0) {
+}
+
+ConvolutionFilter1D::~ConvolutionFilter1D() {
+}
+
+void ConvolutionFilter1D::AddFilter(int filter_offset,
+ const float* filter_values,
+ int filter_length) {
+ SkASSERT(filter_length > 0);
+
+ std::vector<Fixed> fixed_values;
+ fixed_values.reserve(filter_length);
+
+ for (int i = 0; i < filter_length; ++i)
+ fixed_values.push_back(FloatToFixed(filter_values[i]));
+
+ AddFilter(filter_offset, &fixed_values[0], filter_length);
+}
+
+void ConvolutionFilter1D::AddFilter(int filter_offset,
+ const Fixed* filter_values,
+ int filter_length) {
+ // It is common for leading/trailing filter values to be zeros. In such
+ // cases it is beneficial to only store the central factors.
+ // For a scaling to 1/4th in each dimension using a Lanczos-2 filter on
+ // a 1080p image this optimization gives a ~10% speed improvement.
+ int filter_size = filter_length;
+ int first_non_zero = 0;
+ while (first_non_zero < filter_length && filter_values[first_non_zero] == 0)
+ first_non_zero++;
+
+ if (first_non_zero < filter_length) {
+ // Here we have at least one non-zero factor.
+ int last_non_zero = filter_length - 1;
+ while (last_non_zero >= 0 && filter_values[last_non_zero] == 0)
+ last_non_zero--;
+
+ filter_offset += first_non_zero;
+ filter_length = last_non_zero + 1 - first_non_zero;
+ SkASSERT(filter_length > 0);
+
+ for (int i = first_non_zero; i <= last_non_zero; i++)
+ filter_values_.push_back(filter_values[i]);
+ } else {
+ // Here all the factors were zeroes.
+ filter_length = 0;
+ }
+
+ FilterInstance instance;
+
+ // We pushed filter_length elements onto filter_values_
+ instance.data_location = (static_cast<int>(filter_values_.size()) -
+ filter_length);
+ instance.offset = filter_offset;
+ instance.trimmed_length = filter_length;
+ instance.length = filter_size;
+ filters_.push_back(instance);
+
+ max_filter_ = std::max(max_filter_, filter_length);
+}
+
+const ConvolutionFilter1D::Fixed* ConvolutionFilter1D::GetSingleFilter(
+ int* specified_filter_length,
+ int* filter_offset,
+ int* filter_length) const {
+ const FilterInstance& filter = filters_[0];
+ *filter_offset = filter.offset;
+ *filter_length = filter.trimmed_length;
+ *specified_filter_length = filter.length;
+ if (filter.trimmed_length == 0)
+ return NULL;
+
+ return &filter_values_[filter.data_location];
+}
+
+typedef void (*ConvolveVertically_pointer)(
+ const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row,
+ bool has_alpha);
+typedef void (*Convolve4RowsHorizontally_pointer)(
+ const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]);
+typedef void (*ConvolveHorizontally_pointer)(
+ const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha);
+
+struct ConvolveProcs {
+ // This is how many extra pixels may be read by the
+ // conolve*horizontally functions.
+ int extra_horizontal_reads;
+ ConvolveVertically_pointer convolve_vertically;
+ Convolve4RowsHorizontally_pointer convolve_4rows_horizontally;
+ ConvolveHorizontally_pointer convolve_horizontally;
+};
+
+void SetupSIMD(ConvolveProcs *procs) {
+#ifdef SIMD_SSE2
+ base::CPU cpu;
+ if (cpu.has_sse2()) {
+ procs->extra_horizontal_reads = 3;
+ procs->convolve_vertically = &ConvolveVertically_SSE2;
+ procs->convolve_4rows_horizontally = &Convolve4RowsHorizontally_SSE2;
+ procs->convolve_horizontally = &ConvolveHorizontally_SSE2;
+ }
+#elif defined SIMD_MIPS_DSPR2
+ procs->extra_horizontal_reads = 3;
+ procs->convolve_vertically = &ConvolveVertically_mips_dspr2;
+ procs->convolve_horizontally = &ConvolveHorizontally_mips_dspr2;
+#endif
+}
+
+void BGRAConvolve2D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolutionFilter1D& filter_x,
+ const ConvolutionFilter1D& filter_y,
+ int output_byte_row_stride,
+ unsigned char* output,
+ bool use_simd_if_possible) {
+ ConvolveProcs simd;
+ simd.extra_horizontal_reads = 0;
+ simd.convolve_vertically = NULL;
+ simd.convolve_4rows_horizontally = NULL;
+ simd.convolve_horizontally = NULL;
+ if (use_simd_if_possible) {
+ SetupSIMD(&simd);
+ }
+
+ int max_y_filter_size = filter_y.max_filter();
+
+ // The next row in the input that we will generate a horizontally
+ // convolved row for. If the filter doesn't start at the beginning of the
+ // image (this is the case when we are only resizing a subset), then we
+ // don't want to generate any output rows before that. Compute the starting
+ // row for convolution as the first pixel for the first vertical filter.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter_y.FilterForValue(0, &filter_offset, &filter_length);
+ int next_x_row = filter_offset;
+
+ // We loop over each row in the input doing a horizontal convolution. This
+ // will result in a horizontally convolved image. We write the results into
+ // a circular buffer of convolved rows and do vertical convolution as rows
+ // are available. This prevents us from having to store the entire
+ // intermediate image and helps cache coherency.
+ // We will need four extra rows to allow horizontal convolution could be done
+ // simultaneously. We also padding each row in row buffer to be aligned-up to
+ // 16 bytes.
+ // TODO(jiesun): We do not use aligned load from row buffer in vertical
+ // convolution pass yet. Somehow Windows does not like it.
+ int row_buffer_width = (filter_x.num_values() + 15) & ~0xF;
+ int row_buffer_height = max_y_filter_size +
+ (simd.convolve_4rows_horizontally ? 4 : 0);
+ CircularRowBuffer row_buffer(row_buffer_width,
+ row_buffer_height,
+ filter_offset);
+
+ // Loop over every possible output row, processing just enough horizontal
+ // convolutions to run each subsequent vertical convolution.
+ SkASSERT(output_byte_row_stride >= filter_x.num_values() * 4);
+ int num_output_rows = filter_y.num_values();
+
+ // We need to check which is the last line to convolve before we advance 4
+ // lines in one iteration.
+ int last_filter_offset, last_filter_length;
+
+ // SSE2 can access up to 3 extra pixels past the end of the
+ // buffer. At the bottom of the image, we have to be careful
+ // not to access data past the end of the buffer. Normally
+ // we fall back to the C++ implementation for the last row.
+ // If the last row is less than 3 pixels wide, we may have to fall
+ // back to the C++ version for more rows. Compute how many
+ // rows we need to avoid the SSE implementation for here.
+ filter_x.FilterForValue(filter_x.num_values() - 1, &last_filter_offset,
+ &last_filter_length);
+ int avoid_simd_rows = 1 + simd.extra_horizontal_reads /
+ (last_filter_offset + last_filter_length);
+
+ filter_y.FilterForValue(num_output_rows - 1, &last_filter_offset,
+ &last_filter_length);
+
+ for (int out_y = 0; out_y < num_output_rows; out_y++) {
+ filter_values = filter_y.FilterForValue(out_y,
+ &filter_offset, &filter_length);
+
+ // Generate output rows until we have enough to run the current filter.
+ while (next_x_row < filter_offset + filter_length) {
+ if (simd.convolve_4rows_horizontally &&
+ next_x_row + 3 < last_filter_offset + last_filter_length -
+ avoid_simd_rows) {
+ const unsigned char* src[4];
+ unsigned char* out_row[4];
+ for (int i = 0; i < 4; ++i) {
+ src[i] = &source_data[(next_x_row + i) * source_byte_row_stride];
+ out_row[i] = row_buffer.AdvanceRow();
+ }
+ simd.convolve_4rows_horizontally(src, filter_x, out_row);
+ next_x_row += 4;
+ } else {
+ // Check if we need to avoid SSE2 for this row.
+ if (simd.convolve_horizontally &&
+ next_x_row < last_filter_offset + last_filter_length -
+ avoid_simd_rows) {
+ simd.convolve_horizontally(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow(), source_has_alpha);
+ } else {
+ if (source_has_alpha) {
+ ConvolveHorizontally<true>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ } else {
+ ConvolveHorizontally<false>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ }
+ }
+ next_x_row++;
+ }
+ }
+
+ // Compute where in the output image this row of final data will go.
+ unsigned char* cur_output_row = &output[out_y * output_byte_row_stride];
+
+ // Get the list of rows that the circular buffer has, in order.
+ int first_row_in_circular_buffer;
+ unsigned char* const* rows_to_convolve =
+ row_buffer.GetRowAddresses(&first_row_in_circular_buffer);
+
+ // Now compute the start of the subset of those rows that the filter
+ // needs.
+ unsigned char* const* first_row_for_filter =
+ &rows_to_convolve[filter_offset - first_row_in_circular_buffer];
+
+ if (simd.convolve_vertically) {
+ simd.convolve_vertically(filter_values, filter_length,
+ first_row_for_filter,
+ filter_x.num_values(), cur_output_row,
+ source_has_alpha);
+ } else {
+ ConvolveVertically(filter_values, filter_length,
+ first_row_for_filter,
+ filter_x.num_values(), cur_output_row,
+ source_has_alpha);
+ }
+ }
+}
+
+void SingleChannelConvolveX1D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ int input_channel_index,
+ int input_channel_count,
+ const ConvolutionFilter1D& filter,
+ const SkISize& image_size,
+ unsigned char* output,
+ int output_byte_row_stride,
+ int output_channel_index,
+ int output_channel_count,
+ bool absolute_values) {
+ int filter_offset, filter_length, filter_size;
+ // Very much unlike BGRAConvolve2D, here we expect to have the same filter
+ // for all pixels.
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.GetSingleFilter(&filter_size, &filter_offset, &filter_length);
+
+ if (filter_values == NULL || image_size.width() < filter_size) {
+ NOTREACHED();
+ return;
+ }
+
+ int centrepoint = filter_length / 2;
+ if (filter_size - filter_offset != 2 * filter_offset) {
+ // This means the original filter was not symmetrical AND
+ // got clipped from one side more than from the other.
+ centrepoint = filter_size / 2 - filter_offset;
+ }
+
+ const unsigned char* source_data_row = source_data;
+ unsigned char* output_row = output;
+
+ for (int r = 0; r < image_size.height(); ++r) {
+ unsigned char* target_byte = output_row + output_channel_index;
+ // Process the lead part, padding image to the left with the first pixel.
+ int c = 0;
+ for (; c < centrepoint; ++c, target_byte += output_channel_count) {
+ int accval = 0;
+ int i = 0;
+ int pixel_byte_index = input_channel_index;
+ for (; i < centrepoint - c; ++i) // Padding part.
+ accval += filter_values[i] * source_data_row[pixel_byte_index];
+
+ for (; i < filter_length; ++i, pixel_byte_index += input_channel_count)
+ accval += filter_values[i] * source_data_row[pixel_byte_index];
+
+ *target_byte = BringBackTo8(accval, absolute_values);
+ }
+
+ // Now for the main event.
+ for (; c < image_size.width() - centrepoint;
+ ++c, target_byte += output_channel_count) {
+ int accval = 0;
+ int pixel_byte_index = (c - centrepoint) * input_channel_count +
+ input_channel_index;
+
+ for (int i = 0; i < filter_length;
+ ++i, pixel_byte_index += input_channel_count) {
+ accval += filter_values[i] * source_data_row[pixel_byte_index];
+ }
+
+ *target_byte = BringBackTo8(accval, absolute_values);
+ }
+
+ for (; c < image_size.width(); ++c, target_byte += output_channel_count) {
+ int accval = 0;
+ int overlap_taps = image_size.width() - c + centrepoint;
+ int pixel_byte_index = (c - centrepoint) * input_channel_count +
+ input_channel_index;
+ int i = 0;
+ for (; i < overlap_taps - 1; ++i, pixel_byte_index += input_channel_count)
+ accval += filter_values[i] * source_data_row[pixel_byte_index];
+
+ for (; i < filter_length; ++i)
+ accval += filter_values[i] * source_data_row[pixel_byte_index];
+
+ *target_byte = BringBackTo8(accval, absolute_values);
+ }
+
+ source_data_row += source_byte_row_stride;
+ output_row += output_byte_row_stride;
+ }
+}
+
+void SingleChannelConvolveY1D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ int input_channel_index,
+ int input_channel_count,
+ const ConvolutionFilter1D& filter,
+ const SkISize& image_size,
+ unsigned char* output,
+ int output_byte_row_stride,
+ int output_channel_index,
+ int output_channel_count,
+ bool absolute_values) {
+ int filter_offset, filter_length, filter_size;
+ // Very much unlike BGRAConvolve2D, here we expect to have the same filter
+ // for all pixels.
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.GetSingleFilter(&filter_size, &filter_offset, &filter_length);
+
+ if (filter_values == NULL || image_size.height() < filter_size) {
+ NOTREACHED();
+ return;
+ }
+
+ int centrepoint = filter_length / 2;
+ if (filter_size - filter_offset != 2 * filter_offset) {
+ // This means the original filter was not symmetrical AND
+ // got clipped from one side more than from the other.
+ centrepoint = filter_size / 2 - filter_offset;
+ }
+
+ for (int c = 0; c < image_size.width(); ++c) {
+ unsigned char* target_byte = output + c * output_channel_count +
+ output_channel_index;
+ int r = 0;
+
+ for (; r < centrepoint; ++r, target_byte += output_byte_row_stride) {
+ int accval = 0;
+ int i = 0;
+ int pixel_byte_index = c * input_channel_count + input_channel_index;
+
+ for (; i < centrepoint - r; ++i) // Padding part.
+ accval += filter_values[i] * source_data[pixel_byte_index];
+
+ for (; i < filter_length; ++i, pixel_byte_index += source_byte_row_stride)
+ accval += filter_values[i] * source_data[pixel_byte_index];
+
+ *target_byte = BringBackTo8(accval, absolute_values);
+ }
+
+ for (; r < image_size.height() - centrepoint;
+ ++r, target_byte += output_byte_row_stride) {
+ int accval = 0;
+ int pixel_byte_index = (r - centrepoint) * source_byte_row_stride +
+ c * input_channel_count + input_channel_index;
+ for (int i = 0; i < filter_length;
+ ++i, pixel_byte_index += source_byte_row_stride) {
+ accval += filter_values[i] * source_data[pixel_byte_index];
+ }
+
+ *target_byte = BringBackTo8(accval, absolute_values);
+ }
+
+ for (; r < image_size.height();
+ ++r, target_byte += output_byte_row_stride) {
+ int accval = 0;
+ int overlap_taps = image_size.height() - r + centrepoint;
+ int pixel_byte_index = (r - centrepoint) * source_byte_row_stride +
+ c * input_channel_count + input_channel_index;
+ int i = 0;
+ for (; i < overlap_taps - 1;
+ ++i, pixel_byte_index += source_byte_row_stride) {
+ accval += filter_values[i] * source_data[pixel_byte_index];
+ }
+
+ for (; i < filter_length; ++i)
+ accval += filter_values[i] * source_data[pixel_byte_index];
+
+ *target_byte = BringBackTo8(accval, absolute_values);
+ }
+ }
+}
+
+void SetUpGaussianConvolutionKernel(ConvolutionFilter1D* filter,
+ float kernel_sigma,
+ bool derivative) {
+ DCHECK(filter != NULL);
+ DCHECK_GT(kernel_sigma, 0.0);
+ const int tail_length = static_cast<int>(4.0f * kernel_sigma + 0.5f);
+ const int kernel_size = tail_length * 2 + 1;
+ const float sigmasq = kernel_sigma * kernel_sigma;
+ std::vector<float> kernel_weights(kernel_size, 0.0);
+ float kernel_sum = 1.0f;
+
+ kernel_weights[tail_length] = 1.0f;
+
+ for (int ii = 1; ii <= tail_length; ++ii) {
+ float v = std::exp(-0.5f * ii * ii / sigmasq);
+ kernel_weights[tail_length + ii] = v;
+ kernel_weights[tail_length - ii] = v;
+ kernel_sum += 2.0f * v;
+ }
+
+ for (int i = 0; i < kernel_size; ++i)
+ kernel_weights[i] /= kernel_sum;
+
+ if (derivative) {
+ kernel_weights[tail_length] = 0.0;
+ for (int ii = 1; ii <= tail_length; ++ii) {
+ float v = sigmasq * kernel_weights[tail_length + ii] / ii;
+ kernel_weights[tail_length + ii] = v;
+ kernel_weights[tail_length - ii] = -v;
+ }
+ }
+
+ filter->AddFilter(0, &kernel_weights[0], kernel_weights.size());
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/convolver.h b/chromium/skia/ext/convolver.h
new file mode 100644
index 00000000000..dd99a7272f9
--- /dev/null
+++ b/chromium/skia/ext/convolver.h
@@ -0,0 +1,235 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_CONVOLVER_H_
+#define SKIA_EXT_CONVOLVER_H_
+
+#include <cmath>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/cpu.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+// We can build SSE2 optimized versions for all x86 CPUs
+// except when building for the IOS emulator.
+#if defined(ARCH_CPU_X86_FAMILY) && !defined(OS_IOS)
+#define SIMD_SSE2 1
+#define SIMD_PADDING 8 // 8 * int16
+#endif
+
+#if defined (ARCH_CPU_MIPS_FAMILY) && \
+ defined(__mips_dsp) && (__mips_dsp_rev >= 2)
+#define SIMD_MIPS_DSPR2 1
+#endif
+// avoid confusion with Mac OS X's math library (Carbon)
+#if defined(__APPLE__)
+#undef FloatToFixed
+#undef FixedToFloat
+#endif
+
+namespace skia {
+
+// Represents a filter in one dimension. Each output pixel has one entry in this
+// object for the filter values contributing to it. You build up the filter
+// list by calling AddFilter for each output pixel (in order).
+//
+// We do 2-dimensional convolution by first convolving each row by one
+// ConvolutionFilter1D, then convolving each column by another one.
+//
+// Entries are stored in fixed point, shifted left by kShiftBits.
+class ConvolutionFilter1D {
+ public:
+ typedef short Fixed;
+
+ // The number of bits that fixed point values are shifted by.
+ enum { kShiftBits = 14 };
+
+ SK_API ConvolutionFilter1D();
+ SK_API ~ConvolutionFilter1D();
+
+ // Convert between floating point and our fixed point representation.
+ static Fixed FloatToFixed(float f) {
+ return static_cast<Fixed>(f * (1 << kShiftBits));
+ }
+ static unsigned char FixedToChar(Fixed x) {
+ return static_cast<unsigned char>(x >> kShiftBits);
+ }
+ static float FixedToFloat(Fixed x) {
+ // The cast relies on Fixed being a short, implying that on
+ // the platforms we care about all (16) bits will fit into
+ // the mantissa of a (32-bit) float.
+ COMPILE_ASSERT(sizeof(Fixed) == 2, fixed_type_should_fit_in_float_mantissa);
+ float raw = static_cast<float>(x);
+ return ldexpf(raw, -kShiftBits);
+ }
+
+ // Returns the maximum pixel span of a filter.
+ int max_filter() const { return max_filter_; }
+
+ // Returns the number of filters in this filter. This is the dimension of the
+ // output image.
+ int num_values() const { return static_cast<int>(filters_.size()); }
+
+ // Appends the given list of scaling values for generating a given output
+ // pixel. |filter_offset| is the distance from the edge of the image to where
+ // the scaling factors start. The scaling factors apply to the source pixels
+ // starting from this position, and going for the next |filter_length| pixels.
+ //
+ // You will probably want to make sure your input is normalized (that is,
+ // all entries in |filter_values| sub to one) to prevent affecting the overall
+ // brighness of the image.
+ //
+ // The filter_length must be > 0.
+ //
+ // This version will automatically convert your input to fixed point.
+ SK_API void AddFilter(int filter_offset,
+ const float* filter_values,
+ int filter_length);
+
+ // Same as the above version, but the input is already fixed point.
+ void AddFilter(int filter_offset,
+ const Fixed* filter_values,
+ int filter_length);
+
+ // Retrieves a filter for the given |value_offset|, a position in the output
+ // image in the direction we're convolving. The offset and length of the
+ // filter values are put into the corresponding out arguments (see AddFilter
+ // above for what these mean), and a pointer to the first scaling factor is
+ // returned. There will be |filter_length| values in this array.
+ inline const Fixed* FilterForValue(int value_offset,
+ int* filter_offset,
+ int* filter_length) const {
+ const FilterInstance& filter = filters_[value_offset];
+ *filter_offset = filter.offset;
+ *filter_length = filter.trimmed_length;
+ if (filter.trimmed_length == 0) {
+ return NULL;
+ }
+ return &filter_values_[filter.data_location];
+ }
+
+ // Retrieves the filter for the offset 0, presumed to be the one and only.
+ // The offset and length of the filter values are put into the corresponding
+ // out arguments (see AddFilter). Note that |filter_legth| and
+ // |specified_filter_length| may be different if leading/trailing zeros of the
+ // original floating point form were clipped.
+ // There will be |filter_length| values in the return array.
+ // Returns NULL if the filter is 0-length (for instance when all floating
+ // point values passed to AddFilter were clipped to 0).
+ SK_API const Fixed* GetSingleFilter(int* specified_filter_length,
+ int* filter_offset,
+ int* filter_length) const;
+
+ inline void PaddingForSIMD() {
+ // Padding |padding_count| of more dummy coefficients after the coefficients
+ // of last filter to prevent SIMD instructions which load 8 or 16 bytes
+ // together to access invalid memory areas. We are not trying to align the
+ // coefficients right now due to the opaqueness of <vector> implementation.
+ // This has to be done after all |AddFilter| calls.
+#ifdef SIMD_PADDING
+ for (int i = 0; i < SIMD_PADDING; ++i)
+ filter_values_.push_back(static_cast<Fixed>(0));
+#endif
+ }
+
+ private:
+ struct FilterInstance {
+ // Offset within filter_values for this instance of the filter.
+ int data_location;
+
+ // Distance from the left of the filter to the center. IN PIXELS
+ int offset;
+
+ // Number of values in this filter instance.
+ int trimmed_length;
+
+ // Filter length as specified. Note that this may be different from
+ // 'trimmed_length' if leading/trailing zeros of the original floating
+ // point form were clipped differently on each tail.
+ int length;
+ };
+
+ // Stores the information for each filter added to this class.
+ std::vector<FilterInstance> filters_;
+
+ // We store all the filter values in this flat list, indexed by
+ // |FilterInstance.data_location| to avoid the mallocs required for storing
+ // each one separately.
+ std::vector<Fixed> filter_values_;
+
+ // The maximum size of any filter we've added.
+ int max_filter_;
+};
+
+// Does a two-dimensional convolution on the given source image.
+//
+// It is assumed the source pixel offsets referenced in the input filters
+// reference only valid pixels, so the source image size is not required. Each
+// row of the source image starts |source_byte_row_stride| after the previous
+// one (this allows you to have rows with some padding at the end).
+//
+// The result will be put into the given output buffer. The destination image
+// size will be xfilter.num_values() * yfilter.num_values() pixels. It will be
+// in rows of exactly xfilter.num_values() * 4 bytes.
+//
+// |source_has_alpha| is a hint that allows us to avoid doing computations on
+// the alpha channel if the image is opaque. If you don't know, set this to
+// true and it will work properly, but setting this to false will be a few
+// percent faster if you know the image is opaque.
+//
+// The layout in memory is assumed to be 4-bytes per pixel in B-G-R-A order
+// (this is ARGB when loaded into 32-bit words on a little-endian machine).
+SK_API void BGRAConvolve2D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolutionFilter1D& xfilter,
+ const ConvolutionFilter1D& yfilter,
+ int output_byte_row_stride,
+ unsigned char* output,
+ bool use_simd_if_possible);
+
+// Does a 1D convolution of the given source image along the X dimension on
+// a single channel of the bitmap.
+//
+// The function uses the same convolution kernel for each pixel. That kernel
+// must be added to |filter| at offset 0. This is a most straightforward
+// implementation of convolution, intended chiefly for development purposes.
+SK_API void SingleChannelConvolveX1D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ int input_channel_index,
+ int input_channel_count,
+ const ConvolutionFilter1D& filter,
+ const SkISize& image_size,
+ unsigned char* output,
+ int output_byte_row_stride,
+ int output_channel_index,
+ int output_channel_count,
+ bool absolute_values);
+
+// Does a 1D convolution of the given source image along the Y dimension on
+// a single channel of the bitmap.
+SK_API void SingleChannelConvolveY1D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ int input_channel_index,
+ int input_channel_count,
+ const ConvolutionFilter1D& filter,
+ const SkISize& image_size,
+ unsigned char* output,
+ int output_byte_row_stride,
+ int output_channel_index,
+ int output_channel_count,
+ bool absolute_values);
+
+// Set up the |filter| instance with a gaussian kernel. |kernel_sigma| is the
+// parameter of gaussian. If |derivative| is true, the kernel will be that of
+// the first derivative. Intended for use with the two routines above.
+SK_API void SetUpGaussianConvolutionKernel(ConvolutionFilter1D* filter,
+ float kernel_sigma,
+ bool derivative);
+
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_H_
diff --git a/chromium/skia/ext/convolver_SSE2.cc b/chromium/skia/ext/convolver_SSE2.cc
new file mode 100644
index 00000000000..a77a1f45c41
--- /dev/null
+++ b/chromium/skia/ext/convolver_SSE2.cc
@@ -0,0 +1,457 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+
+#include "skia/ext/convolver.h"
+#include "skia/ext/convolver_SSE2.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+#include <emmintrin.h> // ARCH_CPU_X86_FAMILY was defined in build/config.h
+
+namespace skia {
+
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+void ConvolveHorizontally_SSE2(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool /*has_alpha*/) {
+ int num_values = filter.num_values();
+
+ int filter_offset, filter_length;
+ __m128i zero = _mm_setzero_si128();
+ __m128i mask[4];
+ // |mask| will be used to decimate all extra filter coefficients that are
+ // loaded by SIMD when |filter_length| is not divisible by 4.
+ // mask[0] is not used in following algorithm.
+ mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
+ mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
+ mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
+
+ // Output one pixel each iteration, calculating all channels (RGBA) together.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ __m128i accum = _mm_setzero_si128();
+
+ // Compute the first pixel in this row that the filter affects. It will
+ // touch |filter_length| pixels (4 bytes each) after this.
+ const __m128i* row_to_filter =
+ reinterpret_cast<const __m128i*>(&src_data[filter_offset << 2]);
+
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < filter_length >> 2; filter_x++) {
+
+ // Load 4 coefficients => duplicate 1st and 2nd of them for all channels.
+ __m128i coeff, coeff16;
+ // [16] xx xx xx xx c3 c2 c1 c0
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // [16] xx xx xx xx c1 c1 c0 c0
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+
+ // Load four pixels => unpack the first two pixels to 16 bits =>
+ // multiply with coefficients => accumulate the convolution result.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src8 = _mm_loadu_si128(row_to_filter);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0*c0 b0*c0 g0*c0 r0*c0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ // [32] a1*c1 b1*c1 g1*c1 r1*c1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ // Duplicate 3rd and 4th coefficients for all channels =>
+ // unpack the 3rd and 4th pixels to 16 bits => multiply with coefficients
+ // => accumulate the convolution results.
+ // [16] xx xx xx xx c3 c3 c2 c2
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+ // [16] a3 g3 b3 r3 a2 g2 b2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2*c2 b2*c2 g2*c2 r2*c2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ // [32] a3*c3 b3*c3 g3*c3 r3*c3
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ // Advance the pixel and coefficients pointers.
+ row_to_filter += 1;
+ filter_values += 4;
+ }
+
+ // When |filter_length| is not divisible by 4, we need to decimate some of
+ // the filter coefficient that was loaded incorrectly to zero; Other than
+ // that the algorithm is same with above, exceot that the 4th pixel will be
+ // always absent.
+ int r = filter_length&3;
+ if (r) {
+ // Note: filter_values must be padded to align_up(filter_offset, 8).
+ __m128i coeff, coeff16;
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // Mask out extra filter taps.
+ coeff = _mm_and_si128(coeff, mask[r]);
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+
+ // Note: line buffer must be padded to align_up(filter_offset, 16).
+ // We resolve this by use C-version for the last horizontal line.
+ __m128i src8 = _mm_loadu_si128(row_to_filter);
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ coeff16 = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ coeff16 = _mm_unpacklo_epi16(coeff16, coeff16);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum = _mm_add_epi32(accum, t);
+ }
+
+ // Shift right for fixed point implementation.
+ accum = _mm_srai_epi32(accum, ConvolutionFilter1D::kShiftBits);
+
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ accum = _mm_packs_epi32(accum, zero);
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ accum = _mm_packus_epi16(accum, zero);
+
+ // Store the pixel value of 32 bits.
+ *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum);
+ out_row += 4;
+ }
+}
+
+// Convolves horizontally along four rows. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
+// refer to that function for detailed comments.
+void Convolve4RowsHorizontally_SSE2(const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]) {
+ int num_values = filter.num_values();
+
+ int filter_offset, filter_length;
+ __m128i zero = _mm_setzero_si128();
+ __m128i mask[4];
+ // |mask| will be used to decimate all extra filter coefficients that are
+ // loaded by SIMD when |filter_length| is not divisible by 4.
+ // mask[0] is not used in following algorithm.
+ mask[1] = _mm_set_epi16(0, 0, 0, 0, 0, 0, 0, -1);
+ mask[2] = _mm_set_epi16(0, 0, 0, 0, 0, 0, -1, -1);
+ mask[3] = _mm_set_epi16(0, 0, 0, 0, 0, -1, -1, -1);
+
+ // Output one pixel each iteration, calculating all channels (RGBA) together.
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+
+ // four pixels in a column per iteration.
+ __m128i accum0 = _mm_setzero_si128();
+ __m128i accum1 = _mm_setzero_si128();
+ __m128i accum2 = _mm_setzero_si128();
+ __m128i accum3 = _mm_setzero_si128();
+ int start = (filter_offset<<2);
+ // We will load and accumulate with four coefficients per iteration.
+ for (int filter_x = 0; filter_x < (filter_length >> 2); filter_x++) {
+ __m128i coeff, coeff16lo, coeff16hi;
+ // [16] xx xx xx xx c3 c2 c1 c0
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // [16] xx xx xx xx c1 c1 c0 c0
+ coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ // [16] c1 c1 c1 c1 c0 c0 c0 c0
+ coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo);
+ // [16] xx xx xx xx c3 c3 c2 c2
+ coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ // [16] c3 c3 c3 c3 c2 c2 c2 c2
+ coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi);
+
+ __m128i src8, src16, mul_hi, mul_lo, t;
+
+#define ITERATION(src, accum) \
+ src8 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src)); \
+ src16 = _mm_unpacklo_epi8(src8, zero); \
+ mul_hi = _mm_mulhi_epi16(src16, coeff16lo); \
+ mul_lo = _mm_mullo_epi16(src16, coeff16lo); \
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ src16 = _mm_unpackhi_epi8(src8, zero); \
+ mul_hi = _mm_mulhi_epi16(src16, coeff16hi); \
+ mul_lo = _mm_mullo_epi16(src16, coeff16hi); \
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t); \
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi); \
+ accum = _mm_add_epi32(accum, t)
+
+ ITERATION(src_data[0] + start, accum0);
+ ITERATION(src_data[1] + start, accum1);
+ ITERATION(src_data[2] + start, accum2);
+ ITERATION(src_data[3] + start, accum3);
+
+ start += 16;
+ filter_values += 4;
+ }
+
+ int r = filter_length & 3;
+ if (r) {
+ // Note: filter_values must be padded to align_up(filter_offset, 8);
+ __m128i coeff;
+ coeff = _mm_loadl_epi64(reinterpret_cast<const __m128i*>(filter_values));
+ // Mask out extra filter taps.
+ coeff = _mm_and_si128(coeff, mask[r]);
+
+ __m128i coeff16lo = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(1, 1, 0, 0));
+ /* c1 c1 c1 c1 c0 c0 c0 c0 */
+ coeff16lo = _mm_unpacklo_epi16(coeff16lo, coeff16lo);
+ __m128i coeff16hi = _mm_shufflelo_epi16(coeff, _MM_SHUFFLE(3, 3, 2, 2));
+ coeff16hi = _mm_unpacklo_epi16(coeff16hi, coeff16hi);
+
+ __m128i src8, src16, mul_hi, mul_lo, t;
+
+ ITERATION(src_data[0] + start, accum0);
+ ITERATION(src_data[1] + start, accum1);
+ ITERATION(src_data[2] + start, accum2);
+ ITERATION(src_data[3] + start, accum3);
+ }
+
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum0 = _mm_packs_epi32(accum0, zero);
+ accum0 = _mm_packus_epi16(accum0, zero);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_packs_epi32(accum1, zero);
+ accum1 = _mm_packus_epi16(accum1, zero);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_packs_epi32(accum2, zero);
+ accum2 = _mm_packus_epi16(accum2, zero);
+ accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits);
+ accum3 = _mm_packs_epi32(accum3, zero);
+ accum3 = _mm_packus_epi16(accum3, zero);
+
+ *(reinterpret_cast<int*>(out_row[0])) = _mm_cvtsi128_si32(accum0);
+ *(reinterpret_cast<int*>(out_row[1])) = _mm_cvtsi128_si32(accum1);
+ *(reinterpret_cast<int*>(out_row[2])) = _mm_cvtsi128_si32(accum2);
+ *(reinterpret_cast<int*>(out_row[3])) = _mm_cvtsi128_si32(accum3);
+
+ out_row[0] += 4;
+ out_row[1] += 4;
+ out_row[2] += 4;
+ out_row[3] += 4;
+ }
+}
+
+// Does vertical convolution to produce one output row. The filter values and
+// length are given in the first two parameters. These are applied to each
+// of the rows pointed to in the |source_data_rows| array, with each row
+// being |pixel_width| wide.
+//
+// The output must have room for |pixel_width * 4| bytes.
+template<bool has_alpha>
+void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row) {
+ int width = pixel_width & ~3;
+
+ __m128i zero = _mm_setzero_si128();
+ __m128i accum0, accum1, accum2, accum3, coeff16;
+ const __m128i* src;
+ // Output four pixels per iteration (16 bytes).
+ for (int out_x = 0; out_x < width; out_x += 4) {
+
+ // Accumulated result for each pixel. 32 bits per RGBA channel.
+ accum0 = _mm_setzero_si128();
+ accum1 = _mm_setzero_si128();
+ accum2 = _mm_setzero_si128();
+ accum3 = _mm_setzero_si128();
+
+ // Convolve with one filter coefficient per iteration.
+ for (int filter_y = 0; filter_y < filter_length; filter_y++) {
+
+ // Duplicate the filter coefficient 8 times.
+ // [16] cj cj cj cj cj cj cj cj
+ coeff16 = _mm_set1_epi16(filter_values[filter_y]);
+
+ // Load four pixels (16 bytes) together.
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ src = reinterpret_cast<const __m128i*>(
+ &source_data_rows[filter_y][out_x << 2]);
+ __m128i src8 = _mm_loadu_si128(src);
+
+ // Unpack 1st and 2nd pixels from 8 bits to 16 bits for each channels =>
+ // multiply with current coefficient => accumulate the result.
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0 b0 g0 r0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum0 = _mm_add_epi32(accum0, t);
+ // [32] a1 b1 g1 r1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum1 = _mm_add_epi32(accum1, t);
+
+ // Unpack 3rd and 4th pixels from 8 bits to 16 bits for each channels =>
+ // multiply with current coefficient => accumulate the result.
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2 b2 g2 r2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum2 = _mm_add_epi32(accum2, t);
+ // [32] a3 b3 g3 r3
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum3 = _mm_add_epi32(accum3, t);
+ }
+
+ // Shift right for fixed point implementation.
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ accum3 = _mm_srai_epi32(accum3, ConvolutionFilter1D::kShiftBits);
+
+ // Packing 32 bits |accum| to 16 bits per channel (signed saturation).
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packs_epi32(accum0, accum1);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ accum2 = _mm_packs_epi32(accum2, accum3);
+
+ // Packing 16 bits |accum| to 8 bits per channel (unsigned saturation).
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packus_epi16(accum0, accum2);
+
+ if (has_alpha) {
+ // Compute the max(ri, gi, bi) for each pixel.
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ __m128i a = _mm_srli_epi32(accum0, 8);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ __m128i b = _mm_max_epu8(a, accum0); // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ a = _mm_srli_epi32(accum0, 16);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ b = _mm_max_epu8(a, b); // Max of r and g and b.
+ // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
+ b = _mm_slli_epi32(b, 24);
+
+ // Make sure the value of alpha channel is always larger than maximum
+ // value of color channels.
+ accum0 = _mm_max_epu8(b, accum0);
+ } else {
+ // Set value of alpha channels to 0xFF.
+ __m128i mask = _mm_set1_epi32(0xff000000);
+ accum0 = _mm_or_si128(accum0, mask);
+ }
+
+ // Store the convolution result (16 bytes) and advance the pixel pointers.
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(out_row), accum0);
+ out_row += 16;
+ }
+
+ // When the width of the output is not divisible by 4, We need to save one
+ // pixel (4 bytes) each time. And also the fourth pixel is always absent.
+ if (pixel_width & 3) {
+ accum0 = _mm_setzero_si128();
+ accum1 = _mm_setzero_si128();
+ accum2 = _mm_setzero_si128();
+ for (int filter_y = 0; filter_y < filter_length; ++filter_y) {
+ coeff16 = _mm_set1_epi16(filter_values[filter_y]);
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ src = reinterpret_cast<const __m128i*>(
+ &source_data_rows[filter_y][width<<2]);
+ __m128i src8 = _mm_loadu_si128(src);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ __m128i src16 = _mm_unpacklo_epi8(src8, zero);
+ __m128i mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ __m128i mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a0 b0 g0 r0
+ __m128i t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum0 = _mm_add_epi32(accum0, t);
+ // [32] a1 b1 g1 r1
+ t = _mm_unpackhi_epi16(mul_lo, mul_hi);
+ accum1 = _mm_add_epi32(accum1, t);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ src16 = _mm_unpackhi_epi8(src8, zero);
+ mul_hi = _mm_mulhi_epi16(src16, coeff16);
+ mul_lo = _mm_mullo_epi16(src16, coeff16);
+ // [32] a2 b2 g2 r2
+ t = _mm_unpacklo_epi16(mul_lo, mul_hi);
+ accum2 = _mm_add_epi32(accum2, t);
+ }
+
+ accum0 = _mm_srai_epi32(accum0, ConvolutionFilter1D::kShiftBits);
+ accum1 = _mm_srai_epi32(accum1, ConvolutionFilter1D::kShiftBits);
+ accum2 = _mm_srai_epi32(accum2, ConvolutionFilter1D::kShiftBits);
+ // [16] a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packs_epi32(accum0, accum1);
+ // [16] a3 b3 g3 r3 a2 b2 g2 r2
+ accum2 = _mm_packs_epi32(accum2, zero);
+ // [8] a3 b3 g3 r3 a2 b2 g2 r2 a1 b1 g1 r1 a0 b0 g0 r0
+ accum0 = _mm_packus_epi16(accum0, accum2);
+ if (has_alpha) {
+ // [8] xx a3 b3 g3 xx a2 b2 g2 xx a1 b1 g1 xx a0 b0 g0
+ __m128i a = _mm_srli_epi32(accum0, 8);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ __m128i b = _mm_max_epu8(a, accum0); // Max of r and g.
+ // [8] xx xx a3 b3 xx xx a2 b2 xx xx a1 b1 xx xx a0 b0
+ a = _mm_srli_epi32(accum0, 16);
+ // [8] xx xx xx max3 xx xx xx max2 xx xx xx max1 xx xx xx max0
+ b = _mm_max_epu8(a, b); // Max of r and g and b.
+ // [8] max3 00 00 00 max2 00 00 00 max1 00 00 00 max0 00 00 00
+ b = _mm_slli_epi32(b, 24);
+ accum0 = _mm_max_epu8(b, accum0);
+ } else {
+ __m128i mask = _mm_set1_epi32(0xff000000);
+ accum0 = _mm_or_si128(accum0, mask);
+ }
+
+ for (int out_x = width; out_x < pixel_width; out_x++) {
+ *(reinterpret_cast<int*>(out_row)) = _mm_cvtsi128_si32(accum0);
+ accum0 = _mm_srli_si128(accum0, 4);
+ out_row += 4;
+ }
+ }
+}
+
+void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row,
+ bool has_alpha) {
+ if (has_alpha) {
+ ConvolveVertically_SSE2<true>(filter_values,
+ filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row);
+ } else {
+ ConvolveVertically_SSE2<false>(filter_values,
+ filter_length,
+ source_data_rows,
+ pixel_width,
+ out_row);
+ }
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/convolver_SSE2.h b/chromium/skia/ext/convolver_SSE2.h
new file mode 100644
index 00000000000..cf604067e92
--- /dev/null
+++ b/chromium/skia/ext/convolver_SSE2.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_CONVOLVER_SSE2_H_
+#define SKIA_EXT_CONVOLVER_SSE2_H_
+
+#include "skia/ext/convolver.h"
+
+namespace skia {
+
+void ConvolveVertically_SSE2(const ConvolutionFilter1D::Fixed* filter_values,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row,
+ bool has_alpha);
+void Convolve4RowsHorizontally_SSE2(const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]);
+void ConvolveHorizontally_SSE2(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha);
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_SSE2_H_
diff --git a/chromium/skia/ext/convolver_mips_dspr2.cc b/chromium/skia/ext/convolver_mips_dspr2.cc
new file mode 100644
index 00000000000..955abef7a52
--- /dev/null
+++ b/chromium/skia/ext/convolver_mips_dspr2.cc
@@ -0,0 +1,478 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include "skia/ext/convolver.h"
+#include "skia/ext/convolver_mips_dspr2.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+namespace skia {
+// Convolves horizontally along a single row. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+void ConvolveHorizontally_mips_dspr2(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha) {
+#if SIMD_MIPS_DSPR2
+ int row_to_filter = 0;
+ int num_values = filter.num_values();
+ if (has_alpha) {
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ // Get the filter that determines the current output pixel.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+ int filter_x = 0;
+
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set noreorder \n"
+
+ "beqz %[filter_len], 3f \n"
+ " sll $t0, %[filter_offset], 2 \n"
+ "addu %[rtf], %[src_data], $t0 \n"
+ "mtlo $0, $ac0 \n"
+ "mtlo $0, $ac1 \n"
+ "mtlo $0, $ac2 \n"
+ "mtlo $0, $ac3 \n"
+ "srl $t7, %[filter_len], 2 \n"
+ "beqz $t7, 2f \n"
+ " li %[fx], 0 \n"
+
+ "11: \n"
+ "addu $t4, %[filter_val], %[fx] \n"
+ "sll $t5, %[fx], 1 \n"
+ "ulw $t6, 0($t4) \n" // t6 = |cur[1]|cur[0]|
+ "ulw $t8, 4($t4) \n" // t8 = |cur[3]|cur[2]|
+ "addu $t0, %[rtf], $t5 \n"
+ "lw $t1, 0($t0) \n" // t1 = |a0|b0|g0|r0|
+ "lw $t2, 4($t0) \n" // t2 = |a1|b1|g1|r1|
+ "lw $t3, 8($t0) \n" // t3 = |a2|b2|g2|r2|
+ "lw $t4, 12($t0) \n" // t4 = |a3|b3|g3|r3|
+ "precrq.qb.ph $t0, $t2, $t1 \n" // t0 = |a1|g1|a0|g0|
+ "precr.qb.ph $t5, $t2, $t1 \n" // t5 = |b1|r1|b0|r0|
+ "preceu.ph.qbla $t1, $t0 \n" // t1 = |0|a1|0|a0|
+ "preceu.ph.qbra $t2, $t0 \n" // t2 = |0|g1|0|g0|
+ "preceu.ph.qbla $t0, $t5 \n" // t0 = |0|b1|0|b0|
+ "preceu.ph.qbra $t5, $t5 \n" // t5 = |0|r1|0|r0|
+ "dpa.w.ph $ac0, $t1, $t6 \n" // ac0+(cur*a1)+(cur*a0)
+ "dpa.w.ph $ac1, $t0, $t6 \n" // ac1+(cur*b1)+(cur*b0)
+ "dpa.w.ph $ac2, $t2, $t6 \n" // ac2+(cur*g1)+(cur*g0)
+ "dpa.w.ph $ac3, $t5, $t6 \n" // ac3+(cur*r1)+(cur*r0)
+ "precrq.qb.ph $t0, $t4, $t3 \n" // t0 = |a3|g3|a2|g2|
+ "precr.qb.ph $t5, $t4, $t3 \n" // t5 = |b3|r3|b2|r2|
+ "preceu.ph.qbla $t1, $t0 \n" // t1 = |0|a3|0|a2|
+ "preceu.ph.qbra $t2, $t0 \n" // t2 = |0|g3|0|g2|
+ "preceu.ph.qbla $t0, $t5 \n" // t0 = |0|b3|0|b2|
+ "preceu.ph.qbra $t5, $t5 \n" // t5 = |0|r3|0|r2|
+ "dpa.w.ph $ac0, $t1, $t8 \n" // ac0+(cur*a3)+(cur*a2)
+ "dpa.w.ph $ac1, $t0, $t8 \n" // ac1+(cur*b3)+(cur*b2)
+ "dpa.w.ph $ac2, $t2, $t8 \n" // ac2+(cur*g3)+(cur*g2)
+ "dpa.w.ph $ac3, $t5, $t8 \n" // ac3+(cur*r3)+(cur*r2)
+ "addiu $t7, $t7, -1 \n"
+ "bgtz $t7, 11b \n"
+ " addiu %[fx], %[fx], 8 \n"
+
+ "2: \n"
+ "andi $t7, %[filter_len], 0x3 \n" // residual
+ "beqz $t7, 3f \n"
+ " nop \n"
+
+ "21: \n"
+ "sll $t1, %[fx], 1 \n"
+ "addu $t2, %[filter_val], %[fx] \n"
+ "addu $t0, %[rtf], $t1 \n"
+ "lh $t6, 0($t2) \n" // t6 = filter_val[fx]
+ "lbu $t1, 0($t0) \n" // t1 = row[fx * 4 + 0]
+ "lbu $t2, 1($t0) \n" // t2 = row[fx * 4 + 1]
+ "lbu $t3, 2($t0) \n" // t3 = row[fx * 4 + 2]
+ "lbu $t4, 3($t0) \n" // t4 = row[fx * 4 + 2]
+ "maddu $ac3, $t6, $t1 \n"
+ "maddu $ac2, $t6, $t2 \n"
+ "maddu $ac1, $t6, $t3 \n"
+ "maddu $ac0, $t6, $t4 \n"
+ "addiu $t7, $t7, -1 \n"
+ "bgtz $t7, 21b \n"
+ " addiu %[fx], %[fx], 2 \n"
+
+ "3: \n"
+ "extrv.w $t0, $ac0, %[kShiftBits] \n" // a >> kShiftBits
+ "extrv.w $t1, $ac1, %[kShiftBits] \n" // b >> kShiftBits
+ "extrv.w $t2, $ac2, %[kShiftBits] \n" // g >> kShiftBits
+ "extrv.w $t3, $ac3, %[kShiftBits] \n" // r >> kShiftBits
+ "sll $t5, %[out_x], 2 \n"
+ "repl.ph $t6, 128 \n" // t6 = | 128 | 128 |
+ "addu $t5, %[out_row], $t5 \n"
+ "append $t2, $t3, 16 \n"
+ "append $t0, $t1, 16 \n"
+ "subu.ph $t1, $t0, $t6 \n"
+ "shll_s.ph $t1, $t1, 8 \n"
+ "shra.ph $t1, $t1, 8 \n"
+ "addu.ph $t1, $t1, $t6 \n"
+ "subu.ph $t3, $t2, $t6 \n"
+ "shll_s.ph $t3, $t3, 8 \n"
+ "shra.ph $t3, $t3, 8 \n"
+ "addu.ph $t3, $t3, $t6 \n"
+ "precr.qb.ph $t0, $t1, $t3 \n"
+ "usw $t0, 0($t5) \n"
+
+ ".set pop \n"
+ : [fx] "+r" (filter_x), [out_x] "+r" (out_x), [out_row] "+r" (out_row),
+ [rtf] "+r" (row_to_filter)
+ : [filter_val] "r" (filter_values), [filter_len] "r" (filter_length),
+ [kShiftBits] "r" (ConvolutionFilter1D::kShiftBits),
+ [filter_offset] "r" (filter_offset), [src_data] "r" (src_data)
+ : "lo", "hi", "$ac1lo", "$ac1hi", "$ac2lo", "$ac2hi", "$ac3lo", "$ac3hi",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8"
+ );
+ }
+ } else {
+ for (int out_x = 0; out_x < num_values; out_x++) {
+ // Get the filter that determines the current output pixel.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter.FilterForValue(out_x, &filter_offset, &filter_length);
+ int filter_x = 0;
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set noreorder \n"
+
+ "beqz %[filter_len], 3f \n"
+ " sll $t0, %[filter_offset], 2 \n"
+ "addu %[rtf], %[src_data], $t0 \n"
+ "mtlo $0, $ac1 \n"
+ "mtlo $0, $ac2 \n"
+ "mtlo $0, $ac3 \n"
+ "srl $t7, %[filter_len], 2 \n"
+ "beqz $t7, 2f \n"
+ " li %[fx], 0 \n"
+
+ "11: \n"
+ "addu $t4, %[filter_val], %[fx] \n"
+ "sll $t5, %[fx], 1 \n"
+ "ulw $t6, 0($t4) \n" // t6 = |cur[1]|cur[0]|
+ "ulw $t8, 4($t4) \n" // t8 = |cur[3]|cur[2]|
+ "addu $t0, %[rtf], $t5 \n"
+ "lw $t1, 0($t0) \n" // t1 = |a0|b0|g0|r0|
+ "lw $t2, 4($t0) \n" // t2 = |a1|b1|g1|r1|
+ "lw $t3, 8($t0) \n" // t3 = |a2|b2|g2|r2|
+ "lw $t4, 12($t0) \n" // t4 = |a3|b3|g3|r3|
+ "precrq.qb.ph $t0, $t2, $t1 \n" // t0 = |a1|g1|a0|g0|
+ "precr.qb.ph $t5, $t2, $t1 \n" // t5 = |b1|r1|b0|r0|
+ "preceu.ph.qbra $t2, $t0 \n" // t2 = |0|g1|0|g0|
+ "preceu.ph.qbla $t0, $t5 \n" // t0 = |0|b1|0|b0|
+ "preceu.ph.qbra $t5, $t5 \n" // t5 = |0|r1|0|r0|
+ "dpa.w.ph $ac1, $t0, $t6 \n" // ac1+(cur*b1)+(cur*b0)
+ "dpa.w.ph $ac2, $t2, $t6 \n" // ac2+(cur*g1)+(cur*g0)
+ "dpa.w.ph $ac3, $t5, $t6 \n" // ac3+(cur*r1)+(cur*r0)
+ "precrq.qb.ph $t0, $t4, $t3 \n" // t0 = |a3|g3|a2|g2|
+ "precr.qb.ph $t5, $t4, $t3 \n" // t5 = |b3|r3|b2|r2|
+ "preceu.ph.qbra $t2, $t0 \n" // t2 = |0|g3|0|g2|
+ "preceu.ph.qbla $t0, $t5 \n" // t0 = |0|b3|0|b2|
+ "preceu.ph.qbra $t5, $t5 \n" // t5 = |0|r3|0|r2|
+ "dpa.w.ph $ac1, $t0, $t8 \n" // ac1+(cur*b3)+(cur*b2)
+ "dpa.w.ph $ac2, $t2, $t8 \n" // ac2+(cur*g3)+(cur*g2)
+ "dpa.w.ph $ac3, $t5, $t8 \n" // ac3+(cur*r3)+(cur*r2)
+ "addiu $t7, $t7, -1 \n"
+ "bgtz $t7, 11b \n"
+ " addiu %[fx], %[fx], 8 \n"
+
+ "2: \n"
+ "andi $t7, %[filter_len], 0x3 \n" // residual
+ "beqz $t7, 3f \n"
+ " nop \n"
+
+ "21: \n"
+ "sll $t1, %[fx], 1 \n"
+ "addu $t2, %[filter_val], %[fx] \n"
+ "addu $t0, %[rtf], $t1 \n"
+ "lh $t6, 0($t2) \n" // t6 = filter_val[fx]
+ "lbu $t1, 0($t0) \n" // t1 = row[fx * 4 + 0]
+ "lbu $t2, 1($t0) \n" // t2 = row[fx * 4 + 1]
+ "lbu $t3, 2($t0) \n" // t3 = row[fx * 4 + 2]
+ "maddu $ac3, $t6, $t1 \n"
+ "maddu $ac2, $t6, $t2 \n"
+ "maddu $ac1, $t6, $t3 \n"
+ "addiu $t7, $t7, -1 \n"
+ "bgtz $t7, 21b \n"
+ " addiu %[fx], %[fx], 2 \n"
+
+ "3: \n"
+ "extrv.w $t1, $ac1, %[kShiftBits] \n" // b >> kShiftBits
+ "extrv.w $t2, $ac2, %[kShiftBits] \n" // g >> kShiftBits
+ "extrv.w $t3, $ac3, %[kShiftBits] \n" // r >> kShiftBits
+ "repl.ph $t6, 128 \n" // t6 = | 128 | 128 |
+ "sll $t8, %[out_x], 2 \n"
+ "addu $t8, %[out_row], $t8 \n"
+ "append $t2, $t3, 16 \n"
+ "andi $t1, 0xFFFF \n"
+ "subu.ph $t5, $t1, $t6 \n"
+ "shll_s.ph $t5, $t5, 8 \n"
+ "shra.ph $t5, $t5, 8 \n"
+ "addu.ph $t5, $t5, $t6 \n"
+ "subu.ph $t4, $t2, $t6 \n"
+ "shll_s.ph $t4, $t4, 8 \n"
+ "shra.ph $t4, $t4, 8 \n"
+ "addu.ph $t4, $t4, $t6 \n"
+ "precr.qb.ph $t0, $t5, $t4 \n"
+ "usw $t0, 0($t8) \n"
+
+ ".set pop \n"
+ : [fx] "+r" (filter_x), [out_x] "+r" (out_x), [out_row] "+r" (out_row),
+ [rtf] "+r" (row_to_filter)
+ : [filter_val] "r" (filter_values), [filter_len] "r" (filter_length),
+ [kShiftBits] "r" (ConvolutionFilter1D::kShiftBits),
+ [filter_offset] "r" (filter_offset), [src_data] "r" (src_data)
+ : "lo", "hi", "$ac1lo", "$ac1hi", "$ac2lo", "$ac2hi", "$ac3lo", "$ac3hi",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8"
+ );
+ }
+ }
+#endif
+}
+void ConvolveVertically_mips_dspr2(const ConvolutionFilter1D::Fixed* filter_val,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row,
+ bool has_alpha) {
+#if SIMD_MIPS_DSPR2
+ // We go through each column in the output and do a vertical convolution,
+ // generating one output pixel each time.
+ int byte_offset;
+ int cnt;
+ int filter_y;
+ if (has_alpha) {
+ for (int out_x = 0; out_x < pixel_width; out_x++) {
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set noreorder \n"
+
+ "beqz %[filter_len], 3f \n"
+ " sll %[offset], %[out_x], 2 \n"
+ "mtlo $0, $ac0 \n"
+ "mtlo $0, $ac1 \n"
+ "mtlo $0, $ac2 \n"
+ "mtlo $0, $ac3 \n"
+ "srl %[cnt], %[filter_len], 2 \n"
+ "beqz %[cnt], 2f \n"
+ " li %[fy], 0 \n"
+
+ "11: \n"
+ "sll $t1, %[fy], 1 \n"
+ "addu $t0, %[src_data_rows], $t1 \n"
+ "lw $t1, 0($t0) \n"
+ "lw $t2, 4($t0) \n"
+ "lw $t3, 8($t0) \n"
+ "lw $t4, 12($t0) \n"
+ "addu $t1, $t1, %[offset] \n"
+ "addu $t2, $t2, %[offset] \n"
+ "addu $t3, $t3, %[offset] \n"
+ "addu $t4, $t4, %[offset] \n"
+ "lw $t1, 0($t1) \n" // t1 = |a0|b0|g0|r0|
+ "lw $t2, 0($t2) \n" // t2 = |a1|b1|g1|r1|
+ "lw $t3, 0($t3) \n" // t3 = |a0|b0|g0|r0|
+ "lw $t4, 0($t4) \n" // t4 = |a1|b1|g1|r1|
+ "precrq.qb.ph $t5, $t2, $t1 \n" // t5 = |a1|g1|a0|g0|
+ "precr.qb.ph $t6, $t2, $t1 \n" // t6 = |b1|r1|b0|r0|
+ "preceu.ph.qbla $t0, $t5 \n" // t0 = |0|a1|0|a0|
+ "preceu.ph.qbra $t1, $t5 \n" // t1 = |0|g1|0|g0|
+ "preceu.ph.qbla $t2, $t6 \n" // t2 = |0|b1|0|b0|
+ "preceu.ph.qbra $t5, $t6 \n" // t5 = |0|r1|0|r0|
+ "addu $t6, %[filter_val], %[fy] \n"
+ "ulw $t7, 0($t6) \n" // t7 = |cur_1|cur_0|
+ "ulw $t6, 4($t6) \n" // t6 = |cur_3|cur_2|
+ "dpa.w.ph $ac0, $t5, $t7 \n" // (cur*r1)+(cur*r0)
+ "dpa.w.ph $ac1, $t1, $t7 \n" // (cur*g1)+(cur*g0)
+ "dpa.w.ph $ac2, $t2, $t7 \n" // (cur*b1)+(cur*b0)
+ "dpa.w.ph $ac3, $t0, $t7 \n" // (cur*a1)+(cur*a0)
+ "precrq.qb.ph $t5, $t4, $t3 \n" // t5 = |a3|g3|a2|g2|
+ "precr.qb.ph $t7, $t4, $t3 \n" // t7 = |b3|r3|b2|r2|
+ "preceu.ph.qbla $t0, $t5 \n" // t0 = |0|a3|0|a2|
+ "preceu.ph.qbra $t1, $t5 \n" // t1 = |0|g3|0|g2|
+ "preceu.ph.qbla $t2, $t7 \n" // t2 = |0|b3|0|b2|
+ "preceu.ph.qbra $t5, $t7 \n" // t5 = |0|r3|0|r2|
+ "dpa.w.ph $ac0, $t5, $t6 \n" // (cur*r3)+(cur*r2)
+ "dpa.w.ph $ac1, $t1, $t6 \n" // (cur*g3)+(cur*g2)
+ "dpa.w.ph $ac2, $t2, $t6 \n" // (cur*b3)+(cur*b2)
+ "dpa.w.ph $ac3, $t0, $t6 \n" // (cur*a3)+(cur*a2)
+ "addiu %[cnt], %[cnt], -1 \n"
+ "bgtz %[cnt], 11b \n"
+ " addiu %[fy], %[fy], 8 \n"
+
+ "2: \n"
+ "andi %[cnt], %[filter_len], 0x3 \n" // residual
+ "beqz %[cnt], 3f \n"
+ " nop \n"
+
+ "21: \n"
+ "addu $t0, %[filter_val], %[fy] \n"
+ "lh $t4, 0($t0) \n" // t4=filter_val[fx]
+ "sll $t1, %[fy], 1 \n"
+ "addu $t0, %[src_data_rows], $t1 \n"
+ "lw $t1, 0($t0) \n"
+ "addu $t0, $t1, %[offset] \n"
+ "lbu $t1, 0($t0) \n" // t1 = row[fx*4 + 0]
+ "lbu $t2, 1($t0) \n" // t2 = row[fx*4 + 1]
+ "lbu $t3, 2($t0) \n" // t3 = row[fx*4 + 2]
+ "lbu $t0, 3($t0) \n" // t4 = row[fx*4 + 2]
+ "maddu $ac0, $t4, $t1 \n"
+ "maddu $ac1, $t4, $t2 \n"
+ "maddu $ac2, $t4, $t3 \n"
+ "maddu $ac3, $t4, $t0 \n"
+ "addiu %[cnt], %[cnt], -1 \n"
+ "bgtz %[cnt], 21b \n"
+ " addiu %[fy], %[fy], 2 \n"
+
+ "3: \n"
+ "extrv.w $t3, $ac0, %[kShiftBits] \n" // a >> kShiftBits
+ "extrv.w $t2, $ac1, %[kShiftBits] \n" // b >> kShiftBits
+ "extrv.w $t1, $ac2, %[kShiftBits] \n" // g >> kShiftBits
+ "extrv.w $t0, $ac3, %[kShiftBits] \n" // r >> kShiftBits
+ "repl.ph $t4, 128 \n" // t4 = | 128 | 128 |
+ "addu $t5, %[out_row], %[offset] \n"
+ "append $t2, $t3, 16 \n" // t2 = |0|g|0|r|
+ "append $t0, $t1, 16 \n" // t0 = |0|a|0|b|
+ "subu.ph $t1, $t0, $t4 \n"
+ "shll_s.ph $t1, $t1, 8 \n"
+ "shra.ph $t1, $t1, 8 \n"
+ "addu.ph $t1, $t1, $t4 \n" // Clamp(a)|Clamp(b)
+ "subu.ph $t2, $t2, $t4 \n"
+ "shll_s.ph $t2, $t2, 8 \n"
+ "shra.ph $t2, $t2, 8 \n"
+ "addu.ph $t2, $t2, $t4 \n" // Clamp(g)|Clamp(r)
+ "andi $t3, $t1, 0xFF \n" // t3 = ClampTo8(b)
+ "cmp.lt.ph $t3, $t2 \n" // cmp b, g, r
+ "pick.ph $t0, $t2, $t3 \n"
+ "andi $t3, $t0, 0xFF \n"
+ "srl $t4, $t0, 16 \n"
+ "cmp.lt.ph $t3, $t4 \n"
+ "pick.ph $t0, $t4, $t3 \n" // t0 = max_color_ch
+ "srl $t3, $t1, 16 \n" // t1 = ClampTo8(a)
+ "cmp.lt.ph $t3, $t0 \n"
+ "pick.ph $t0, $t0, $t3 \n"
+ "ins $t1, $t0, 16, 8 \n"
+ "precr.qb.ph $t0, $t1, $t2 \n" // t0 = |a|b|g|r|
+ "usw $t0, 0($t5) \n"
+
+ ".set pop \n"
+ : [filter_val] "+r" (filter_val), [filter_len] "+r" (filter_length),
+ [offset] "+r" (byte_offset), [fy] "+r" (filter_y), [cnt] "+r" (cnt),
+ [out_x] "+r" (out_x), [pixel_width] "+r" (pixel_width)
+ : [src_data_rows] "r" (source_data_rows), [out_row] "r" (out_row),
+ [kShiftBits] "r" (ConvolutionFilter1D::kShiftBits)
+ : "lo", "hi", "$ac1lo", "$ac1hi", "$ac2lo", "$ac2hi", "$ac3lo", "$ac3hi",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6","t7", "memory"
+ );
+ }
+ } else {
+ for (int out_x = 0; out_x < pixel_width; out_x++) {
+ __asm__ __volatile__ (
+ ".set push \n"
+ ".set noreorder \n"
+
+ "beqz %[filter_len], 3f \n"
+ " sll %[offset], %[out_x], 2 \n"
+ "mtlo $0, $ac0 \n"
+ "mtlo $0, $ac1 \n"
+ "mtlo $0, $ac2 \n"
+ "srl %[cnt], %[filter_len], 2 \n"
+ "beqz %[cnt], 2f \n"
+ " li %[fy], 0 \n"
+
+ "11: \n"
+ "sll $t1, %[fy], 1 \n"
+ "addu $t0, %[src_data_rows], $t1 \n"
+ "lw $t1, 0($t0) \n"
+ "lw $t2, 4($t0) \n"
+ "lw $t3, 8($t0) \n"
+ "lw $t4, 12($t0) \n"
+ "addu $t1, $t1, %[offset] \n"
+ "addu $t2, $t2, %[offset] \n"
+ "addu $t3, $t3, %[offset] \n"
+ "addu $t4, $t4, %[offset] \n"
+ "lw $t1, 0($t1) \n" // t1 = |a0|b0|g0|r0|
+ "lw $t2, 0($t2) \n" // t2 = |a1|b1|g1|r1|
+ "lw $t3, 0($t3) \n" // t3 = |a0|b0|g0|r0|
+ "lw $t4, 0($t4) \n" // t4 = |a1|b1|g1|r1|
+ "precrq.qb.ph $t5, $t2, $t1 \n" // t5 = |a1|g1|a0|g0|
+ "precr.qb.ph $t6, $t2, $t1 \n" // t6 = |b1|r1|b0|r0|
+ "preceu.ph.qbra $t1, $t5 \n" // t1 = |0|g1|0|g0|
+ "preceu.ph.qbla $t2, $t6 \n" // t2 = |0|b1|0|b0|
+ "preceu.ph.qbra $t5, $t6 \n" // t5 = |0|r1|0|r0|
+ "addu $t6, %[filter_val], %[fy] \n"
+ "ulw $t0, 0($t6) \n" // t0 = |cur_1|cur_0|
+ "ulw $t6, 4($t6) \n" // t6 = |cur_1|cur_0|
+ "dpa.w.ph $ac0, $t5, $t0 \n" // (cur*r1)+(cur*r0)
+ "dpa.w.ph $ac1, $t1, $t0 \n" // (cur*g1)+(cur*g0)
+ "dpa.w.ph $ac2, $t2, $t0 \n" // (cur*b1)+(cur*b0)
+ "precrq.qb.ph $t5, $t4, $t3 \n" // t5 = |a3|g3|a2|g2|
+ "precr.qb.ph $t0, $t4, $t3 \n" // t0 = |b3|r3|b2|r2|
+ "preceu.ph.qbra $t1, $t5 \n" // t1 = |0|g3|0|g2|
+ "preceu.ph.qbla $t2, $t0 \n" // t2 = |0|b3|0|b2|
+ "preceu.ph.qbra $t5, $t0 \n" // t5 = |0|r3|0|r2|
+ "dpa.w.ph $ac0, $t5, $t6 \n" // (cur*r1)+(cur*r0)
+ "dpa.w.ph $ac1, $t1, $t6 \n" // (cur*g1)+(cur*g0)
+ "dpa.w.ph $ac2, $t2, $t6 \n" // (cur*b1)+(cur*b0)
+ "addiu %[cnt], %[cnt], -1 \n"
+ "bgtz %[cnt], 11b \n"
+ " addiu %[fy], %[fy], 8 \n"
+
+ "2: \n"
+ "andi %[cnt], %[filter_len], 0x3 \n" // residual
+ "beqz %[cnt], 3f \n"
+ " nop \n"
+
+ "21: \n"
+ "addu $t0, %[filter_val], %[fy] \n"
+ "lh $t4, 0($t0) \n" // filter_val[fx]
+ "sll $t1, %[fy], 1 \n"
+ "addu $t0, %[src_data_rows], $t1 \n"
+ "lw $t1, 0($t0) \n"
+ "addu $t0, $t1, %[offset] \n"
+ "lbu $t1, 0($t0) \n" // t1 = row[fx*4 + 0]
+ "lbu $t2, 1($t0) \n" // t2 = row[fx*4 + 1]
+ "lbu $t3, 2($t0) \n" // t3 = row[fx*4 + 2]
+ "maddu $ac0, $t4, $t1 \n"
+ "maddu $ac1, $t4, $t2 \n"
+ "maddu $ac2, $t4, $t3 \n"
+ "addiu %[cnt], %[cnt], -1 \n"
+ "bgtz %[cnt], 21b \n"
+ " addiu %[fy], %[fy], 2 \n"
+
+ "3: \n"
+ "extrv.w $t3, $ac0, %[kShiftBits] \n" // r >> kShiftBits
+ "extrv.w $t2, $ac1, %[kShiftBits] \n" // g >> kShiftBits
+ "extrv.w $t1, $ac2, %[kShiftBits] \n" // b >> kShiftBits
+ "repl.ph $t6, 128 \n" // t6 = | 128 | 128 |
+ "addu $t5, %[out_row], %[offset] \n"
+ "append $t2, $t3, 16 \n" // t2 = |0|g|0|r|
+ "andi $t1, $t1, 0xFFFF \n"
+ "subu.ph $t1, $t1, $t6 \n"
+ "shll_s.ph $t1, $t1, 8 \n"
+ "shra.ph $t1, $t1, 8 \n"
+ "addu.ph $t1, $t1, $t6 \n" // Clamp(a)|Clamp(b)
+ "subu.ph $t2, $t2, $t6 \n"
+ "shll_s.ph $t2, $t2, 8 \n"
+ "shra.ph $t2, $t2, 8 \n"
+ "addu.ph $t2, $t2, $t6 \n" // Clamp(g)|Clamp(r)
+ "li $t0, 0xFF \n"
+ "ins $t1, $t0, 16, 8 \n"
+ "precr.qb.ph $t0, $t1, $t2 \n" // t0 = |a|b|g|r|
+ "usw $t0, 0($t5) \n"
+
+ ".set pop \n"
+ : [filter_val] "+r" (filter_val), [filter_len] "+r" (filter_length),
+ [offset] "+r" (byte_offset), [fy] "+r" (filter_y), [cnt] "+r" (cnt),
+ [out_x] "+r" (out_x), [pixel_width] "+r" (pixel_width)
+ : [src_data_rows] "r" (source_data_rows), [out_row] "r" (out_row),
+ [kShiftBits] "r" (ConvolutionFilter1D::kShiftBits)
+ : "lo", "hi", "$ac1lo", "$ac1hi", "$ac2lo", "$ac2hi", "$ac3lo", "$ac3hi",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "memory"
+ );
+ }
+ }
+#endif
+}
+} // namespace skia
diff --git a/chromium/skia/ext/convolver_mips_dspr2.h b/chromium/skia/ext/convolver_mips_dspr2.h
new file mode 100644
index 00000000000..a5e59f935b9
--- /dev/null
+++ b/chromium/skia/ext/convolver_mips_dspr2.h
@@ -0,0 +1,25 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_CONVOLVER_MIPS_DSPR2_H_
+#define SKIA_EXT_CONVOLVER_MIPS_DSPR2_H_
+
+#include "skia/ext/convolver.h"
+
+namespace skia {
+
+void ConvolveVertically_mips_dspr2(const ConvolutionFilter1D::Fixed* filter_val,
+ int filter_length,
+ unsigned char* const* source_data_rows,
+ int pixel_width,
+ unsigned char* out_row,
+ bool has_alpha);
+
+void ConvolveHorizontally_mips_dspr2(const unsigned char* src_data,
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row,
+ bool has_alpha);
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_MIPS_DSPR2_H_
diff --git a/chromium/skia/ext/convolver_unittest.cc b/chromium/skia/ext/convolver_unittest.cc
new file mode 100644
index 00000000000..8d0c852a16d
--- /dev/null
+++ b/chromium/skia/ext/convolver_unittest.cc
@@ -0,0 +1,535 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <string.h>
+#include <time.h>
+#include <algorithm>
+#include <numeric>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "skia/ext/convolver.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+namespace skia {
+
+namespace {
+
+// Fills the given filter with impulse functions for the range 0->num_entries.
+void FillImpulseFilter(int num_entries, ConvolutionFilter1D* filter) {
+ float one = 1.0f;
+ for (int i = 0; i < num_entries; i++)
+ filter->AddFilter(i, &one, 1);
+}
+
+// Filters the given input with the impulse function, and verifies that it
+// does not change.
+void TestImpulseConvolution(const unsigned char* data, int width, int height) {
+ int byte_count = width * height * 4;
+
+ ConvolutionFilter1D filter_x;
+ FillImpulseFilter(width, &filter_x);
+
+ ConvolutionFilter1D filter_y;
+ FillImpulseFilter(height, &filter_y);
+
+ std::vector<unsigned char> output;
+ output.resize(byte_count);
+ BGRAConvolve2D(data, width * 4, true, filter_x, filter_y,
+ filter_x.num_values() * 4, &output[0], false);
+
+ // Output should exactly match input.
+ EXPECT_EQ(0, memcmp(data, &output[0], byte_count));
+}
+
+// Fills the destination filter with a box filter averaging every two pixels
+// to produce the output.
+void FillBoxFilter(int size, ConvolutionFilter1D* filter) {
+ const float box[2] = { 0.5, 0.5 };
+ for (int i = 0; i < size; i++)
+ filter->AddFilter(i * 2, box, 2);
+}
+
+} // namespace
+
+// Tests that each pixel, when set and run through the impulse filter, does
+// not change.
+TEST(Convolver, Impulse) {
+ // We pick an "odd" size that is not likely to fit on any boundaries so that
+ // we can see if all the widths and paddings are handled properly.
+ int width = 15;
+ int height = 31;
+ int byte_count = width * height * 4;
+ std::vector<unsigned char> input;
+ input.resize(byte_count);
+
+ unsigned char* input_ptr = &input[0];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ for (int channel = 0; channel < 3; channel++) {
+ memset(input_ptr, 0, byte_count);
+ input_ptr[(y * width + x) * 4 + channel] = 0xff;
+ // Always set the alpha channel or it will attempt to "fix" it for us.
+ input_ptr[(y * width + x) * 4 + 3] = 0xff;
+ TestImpulseConvolution(input_ptr, width, height);
+ }
+ }
+ }
+}
+
+// Tests that using a box filter to halve an image results in every square of 4
+// pixels in the original get averaged to a pixel in the output.
+TEST(Convolver, Halve) {
+ static const int kSize = 16;
+
+ int src_width = kSize;
+ int src_height = kSize;
+ int src_row_stride = src_width * 4;
+ int src_byte_count = src_row_stride * src_height;
+ std::vector<unsigned char> input;
+ input.resize(src_byte_count);
+
+ int dest_width = src_width / 2;
+ int dest_height = src_height / 2;
+ int dest_byte_count = dest_width * dest_height * 4;
+ std::vector<unsigned char> output;
+ output.resize(dest_byte_count);
+
+ // First fill the array with a bunch of random data.
+ srand(static_cast<unsigned>(time(NULL)));
+ for (int i = 0; i < src_byte_count; i++)
+ input[i] = rand() * 255 / RAND_MAX;
+
+ // Compute the filters.
+ ConvolutionFilter1D filter_x, filter_y;
+ FillBoxFilter(dest_width, &filter_x);
+ FillBoxFilter(dest_height, &filter_y);
+
+ // Do the convolution.
+ BGRAConvolve2D(&input[0], src_width, true, filter_x, filter_y,
+ filter_x.num_values() * 4, &output[0], false);
+
+ // Compute the expected results and check, allowing for a small difference
+ // to account for rounding errors.
+ for (int y = 0; y < dest_height; y++) {
+ for (int x = 0; x < dest_width; x++) {
+ for (int channel = 0; channel < 4; channel++) {
+ int src_offset = (y * 2 * src_row_stride + x * 2 * 4) + channel;
+ int value = input[src_offset] + // Top left source pixel.
+ input[src_offset + 4] + // Top right source pixel.
+ input[src_offset + src_row_stride] + // Lower left.
+ input[src_offset + src_row_stride + 4]; // Lower right.
+ value /= 4; // Average.
+ int difference = value - output[(y * dest_width + x) * 4 + channel];
+ EXPECT_TRUE(difference >= -1 || difference <= 1);
+ }
+ }
+ }
+}
+
+// Tests the optimization in Convolver1D::AddFilter that avoids storing
+// leading/trailing zeroes.
+TEST(Convolver, AddFilter) {
+ skia::ConvolutionFilter1D filter;
+
+ const skia::ConvolutionFilter1D::Fixed* values = NULL;
+ int filter_offset = 0;
+ int filter_length = 0;
+
+ // An all-zero filter is handled correctly, all factors ignored
+ static const float factors1[] = { 0.0f, 0.0f, 0.0f };
+ filter.AddFilter(11, factors1, arraysize(factors1));
+ ASSERT_EQ(0, filter.max_filter());
+ ASSERT_EQ(1, filter.num_values());
+
+ values = filter.FilterForValue(0, &filter_offset, &filter_length);
+ ASSERT_TRUE(values == NULL); // No values => NULL.
+ ASSERT_EQ(11, filter_offset); // Same as input offset.
+ ASSERT_EQ(0, filter_length); // But no factors since all are zeroes.
+
+ // Zeroes on the left are ignored
+ static const float factors2[] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f };
+ filter.AddFilter(22, factors2, arraysize(factors2));
+ ASSERT_EQ(4, filter.max_filter());
+ ASSERT_EQ(2, filter.num_values());
+
+ values = filter.FilterForValue(1, &filter_offset, &filter_length);
+ ASSERT_TRUE(values != NULL);
+ ASSERT_EQ(23, filter_offset); // 22 plus 1 leading zero
+ ASSERT_EQ(4, filter_length); // 5 - 1 leading zero
+
+ // Zeroes on the right are ignored
+ static const float factors3[] = { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f };
+ filter.AddFilter(33, factors3, arraysize(factors3));
+ ASSERT_EQ(5, filter.max_filter());
+ ASSERT_EQ(3, filter.num_values());
+
+ values = filter.FilterForValue(2, &filter_offset, &filter_length);
+ ASSERT_TRUE(values != NULL);
+ ASSERT_EQ(33, filter_offset); // 33, same as input due to no leading zero
+ ASSERT_EQ(5, filter_length); // 7 - 2 trailing zeroes
+
+ // Zeroes in leading & trailing positions
+ static const float factors4[] = { 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f };
+ filter.AddFilter(44, factors4, arraysize(factors4));
+ ASSERT_EQ(5, filter.max_filter()); // No change from existing value.
+ ASSERT_EQ(4, filter.num_values());
+
+ values = filter.FilterForValue(3, &filter_offset, &filter_length);
+ ASSERT_TRUE(values != NULL);
+ ASSERT_EQ(46, filter_offset); // 44 plus 2 leading zeroes
+ ASSERT_EQ(3, filter_length); // 7 - (2 leading + 2 trailing) zeroes
+
+ // Zeroes surrounded by non-zero values are ignored
+ static const float factors5[] = { 0.0f, 0.0f,
+ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f,
+ 0.0f };
+ filter.AddFilter(55, factors5, arraysize(factors5));
+ ASSERT_EQ(6, filter.max_filter());
+ ASSERT_EQ(5, filter.num_values());
+
+ values = filter.FilterForValue(4, &filter_offset, &filter_length);
+ ASSERT_TRUE(values != NULL);
+ ASSERT_EQ(57, filter_offset); // 55 plus 2 leading zeroes
+ ASSERT_EQ(6, filter_length); // 9 - (2 leading + 1 trailing) zeroes
+
+ // All-zero filters after the first one also work
+ static const float factors6[] = { 0.0f };
+ filter.AddFilter(66, factors6, arraysize(factors6));
+ ASSERT_EQ(6, filter.max_filter());
+ ASSERT_EQ(6, filter.num_values());
+
+ values = filter.FilterForValue(5, &filter_offset, &filter_length);
+ ASSERT_TRUE(values == NULL); // filter_length == 0 => values is NULL
+ ASSERT_EQ(66, filter_offset); // value passed in
+ ASSERT_EQ(0, filter_length);
+}
+
+#if defined(THREAD_SANITIZER)
+// Times out under ThreadSanitizer, http://crbug.com/134400.
+#define MAYBE_SIMDVerification DISABLED_SIMDVerification
+#else
+#define MAYBE_SIMDVerification SIMDVerification
+#endif
+TEST(Convolver, MAYBE_SIMDVerification) {
+ int source_sizes[][2] = {
+ {1,1}, {1,2}, {1,3}, {1,4}, {1,5},
+ {2,1}, {2,2}, {2,3}, {2,4}, {2,5},
+ {3,1}, {3,2}, {3,3}, {3,4}, {3,5},
+ {4,1}, {4,2}, {4,3}, {4,4}, {4,5},
+ {1920, 1080},
+ {720, 480},
+ {1377, 523},
+ {325, 241} };
+ int dest_sizes[][2] = { {1280, 1024}, {480, 270}, {177, 123} };
+ float filter[] = { 0.05f, -0.15f, 0.6f, 0.6f, -0.15f, 0.05f };
+
+ srand(static_cast<unsigned int>(time(0)));
+
+ // Loop over some specific source and destination dimensions.
+ for (unsigned int i = 0; i < arraysize(source_sizes); ++i) {
+ unsigned int source_width = source_sizes[i][0];
+ unsigned int source_height = source_sizes[i][1];
+ for (unsigned int j = 0; j < arraysize(dest_sizes); ++j) {
+ unsigned int dest_width = dest_sizes[j][0];
+ unsigned int dest_height = dest_sizes[j][1];
+
+ // Preparing convolve coefficients.
+ ConvolutionFilter1D x_filter, y_filter;
+ for (unsigned int p = 0; p < dest_width; ++p) {
+ unsigned int offset = source_width * p / dest_width;
+ EXPECT_LT(offset, source_width);
+ x_filter.AddFilter(offset, filter,
+ std::min<int>(arraysize(filter),
+ source_width - offset));
+ }
+ x_filter.PaddingForSIMD();
+ for (unsigned int p = 0; p < dest_height; ++p) {
+ unsigned int offset = source_height * p / dest_height;
+ y_filter.AddFilter(offset, filter,
+ std::min<int>(arraysize(filter),
+ source_height - offset));
+ }
+ y_filter.PaddingForSIMD();
+
+ // Allocate input and output skia bitmap.
+ SkBitmap source, result_c, result_sse;
+ source.setConfig(SkBitmap::kARGB_8888_Config,
+ source_width, source_height);
+ source.allocPixels();
+ result_c.setConfig(SkBitmap::kARGB_8888_Config,
+ dest_width, dest_height);
+ result_c.allocPixels();
+ result_sse.setConfig(SkBitmap::kARGB_8888_Config,
+ dest_width, dest_height);
+ result_sse.allocPixels();
+
+ // Randomize source bitmap for testing.
+ unsigned char* src_ptr = static_cast<unsigned char*>(source.getPixels());
+ for (int y = 0; y < source.height(); y++) {
+ for (unsigned int x = 0; x < source.rowBytes(); x++)
+ src_ptr[x] = rand() % 255;
+ src_ptr += source.rowBytes();
+ }
+
+ // Test both cases with different has_alpha.
+ for (int alpha = 0; alpha < 2; alpha++) {
+ // Convolve using C code.
+ base::TimeTicks resize_start;
+ base::TimeDelta delta_c, delta_sse;
+ unsigned char* r1 = static_cast<unsigned char*>(result_c.getPixels());
+ unsigned char* r2 = static_cast<unsigned char*>(result_sse.getPixels());
+
+ resize_start = base::TimeTicks::Now();
+ BGRAConvolve2D(static_cast<const uint8*>(source.getPixels()),
+ static_cast<int>(source.rowBytes()),
+ (alpha != 0), x_filter, y_filter,
+ static_cast<int>(result_c.rowBytes()), r1, false);
+ delta_c = base::TimeTicks::Now() - resize_start;
+
+ resize_start = base::TimeTicks::Now();
+ // Convolve using SSE2 code
+ BGRAConvolve2D(static_cast<const uint8*>(source.getPixels()),
+ static_cast<int>(source.rowBytes()),
+ (alpha != 0), x_filter, y_filter,
+ static_cast<int>(result_sse.rowBytes()), r2, true);
+ delta_sse = base::TimeTicks::Now() - resize_start;
+
+ // Unfortunately I could not enable the performance check now.
+ // Most bots use debug version, and there are great difference between
+ // the code generation for intrinsic, etc. In release version speed
+ // difference was 150%-200% depend on alpha channel presence;
+ // while in debug version speed difference was 96%-120%.
+ // TODO(jiesun): optimize further until we could enable this for
+ // debug version too.
+ // EXPECT_LE(delta_sse, delta_c);
+
+ int64 c_us = delta_c.InMicroseconds();
+ int64 sse_us = delta_sse.InMicroseconds();
+ VLOG(1) << "from:" << source_width << "x" << source_height
+ << " to:" << dest_width << "x" << dest_height
+ << (alpha ? " with alpha" : " w/o alpha");
+ VLOG(1) << "c:" << c_us << " sse:" << sse_us;
+ VLOG(1) << "ratio:" << static_cast<float>(c_us) / sse_us;
+
+ // Comparing result.
+ for (unsigned int i = 0; i < dest_height; i++) {
+ for (unsigned int x = 0; x < dest_width * 4; x++) { // RGBA always.
+ EXPECT_EQ(r1[x], r2[x]);
+ }
+ r1 += result_c.rowBytes();
+ r2 += result_sse.rowBytes();
+ }
+ }
+ }
+ }
+}
+
+TEST(Convolver, SeparableSingleConvolution) {
+ static const int kImgWidth = 1024;
+ static const int kImgHeight = 1024;
+ static const int kChannelCount = 3;
+ static const int kStrideSlack = 22;
+ ConvolutionFilter1D filter;
+ const float box[5] = { 0.2f, 0.2f, 0.2f, 0.2f, 0.2f };
+ filter.AddFilter(0, box, 5);
+
+ // Allocate a source image and set to 0.
+ const int src_row_stride = kImgWidth * kChannelCount + kStrideSlack;
+ int src_byte_count = src_row_stride * kImgHeight;
+ std::vector<unsigned char> input;
+ const int signal_x = kImgWidth / 2;
+ const int signal_y = kImgHeight / 2;
+ input.resize(src_byte_count, 0);
+ // The image has a single impulse pixel in channel 1, smack in the middle.
+ const int non_zero_pixel_index =
+ signal_y * src_row_stride + signal_x * kChannelCount + 1;
+ input[non_zero_pixel_index] = 255;
+
+ // Destination will be a single channel image with stide matching width.
+ const int dest_row_stride = kImgWidth;
+ const int dest_byte_count = dest_row_stride * kImgHeight;
+ std::vector<unsigned char> output;
+ output.resize(dest_byte_count);
+
+ // Apply convolution in X.
+ SingleChannelConvolveX1D(&input[0], src_row_stride, 1, kChannelCount,
+ filter, SkISize::Make(kImgWidth, kImgHeight),
+ &output[0], dest_row_stride, 0, 1, false);
+ for (int x = signal_x - 2; x <= signal_x + 2; ++x)
+ EXPECT_GT(output[signal_y * dest_row_stride + x], 0);
+
+ EXPECT_EQ(output[signal_y * dest_row_stride + signal_x - 3], 0);
+ EXPECT_EQ(output[signal_y * dest_row_stride + signal_x + 3], 0);
+
+ // Apply convolution in Y.
+ SingleChannelConvolveY1D(&input[0], src_row_stride, 1, kChannelCount,
+ filter, SkISize::Make(kImgWidth, kImgHeight),
+ &output[0], dest_row_stride, 0, 1, false);
+ for (int y = signal_y - 2; y <= signal_y + 2; ++y)
+ EXPECT_GT(output[y * dest_row_stride + signal_x], 0);
+
+ EXPECT_EQ(output[(signal_y - 3) * dest_row_stride + signal_x], 0);
+ EXPECT_EQ(output[(signal_y + 3) * dest_row_stride + signal_x], 0);
+
+ EXPECT_EQ(output[signal_y * dest_row_stride + signal_x - 1], 0);
+ EXPECT_EQ(output[signal_y * dest_row_stride + signal_x + 1], 0);
+
+ // The main point of calling this is to invoke the routine on input without
+ // padding.
+ std::vector<unsigned char> output2;
+ output2.resize(dest_byte_count);
+ SingleChannelConvolveX1D(&output[0], dest_row_stride, 0, 1,
+ filter, SkISize::Make(kImgWidth, kImgHeight),
+ &output2[0], dest_row_stride, 0, 1, false);
+ // This should be a result of 2D convolution.
+ for (int x = signal_x - 2; x <= signal_x + 2; ++x) {
+ for (int y = signal_y - 2; y <= signal_y + 2; ++y)
+ EXPECT_GT(output2[y * dest_row_stride + x], 0);
+ }
+ EXPECT_EQ(output2[0], 0);
+ EXPECT_EQ(output2[dest_row_stride - 1], 0);
+ EXPECT_EQ(output2[dest_byte_count - 1], 0);
+}
+
+TEST(Convolver, SeparableSingleConvolutionEdges) {
+ // The purpose of this test is to check if the implementation treats correctly
+ // edges of the image.
+ static const int kImgWidth = 600;
+ static const int kImgHeight = 800;
+ static const int kChannelCount = 3;
+ static const int kStrideSlack = 22;
+ static const int kChannel = 1;
+ ConvolutionFilter1D filter;
+ const float box[5] = { 0.2f, 0.2f, 0.2f, 0.2f, 0.2f };
+ filter.AddFilter(0, box, 5);
+
+ // Allocate a source image and set to 0.
+ int src_row_stride = kImgWidth * kChannelCount + kStrideSlack;
+ int src_byte_count = src_row_stride * kImgHeight;
+ std::vector<unsigned char> input(src_byte_count);
+
+ // Draw a frame around the image.
+ for (int i = 0; i < src_byte_count; ++i) {
+ int row = i / src_row_stride;
+ int col = i % src_row_stride / kChannelCount;
+ int channel = i % src_row_stride % kChannelCount;
+ if (channel != kChannel || col > kImgWidth) {
+ input[i] = 255;
+ } else if (row == 0 || col == 0 ||
+ col == kImgWidth - 1 || row == kImgHeight - 1) {
+ input[i] = 100;
+ } else if (row == 1 || col == 1 ||
+ col == kImgWidth - 2 || row == kImgHeight - 2) {
+ input[i] = 200;
+ } else {
+ input[i] = 0;
+ }
+ }
+
+ // Destination will be a single channel image with stide matching width.
+ int dest_row_stride = kImgWidth;
+ int dest_byte_count = dest_row_stride * kImgHeight;
+ std::vector<unsigned char> output;
+ output.resize(dest_byte_count);
+
+ // Apply convolution in X.
+ SingleChannelConvolveX1D(&input[0], src_row_stride, 1, kChannelCount,
+ filter, SkISize::Make(kImgWidth, kImgHeight),
+ &output[0], dest_row_stride, 0, 1, false);
+
+ // Sadly, comparison is not as simple as retaining all values.
+ int invalid_values = 0;
+ const unsigned char first_value = output[0];
+ EXPECT_NEAR(first_value, 100, 1);
+ for (int i = 0; i < dest_row_stride; ++i) {
+ if (output[i] != first_value)
+ ++invalid_values;
+ }
+ EXPECT_EQ(0, invalid_values);
+
+ int test_row = 22;
+ EXPECT_NEAR(output[test_row * dest_row_stride], 100, 1);
+ EXPECT_NEAR(output[test_row * dest_row_stride + 1], 80, 1);
+ EXPECT_NEAR(output[test_row * dest_row_stride + 2], 60, 1);
+ EXPECT_NEAR(output[test_row * dest_row_stride + 3], 40, 1);
+ EXPECT_NEAR(output[(test_row + 1) * dest_row_stride - 1], 100, 1);
+ EXPECT_NEAR(output[(test_row + 1) * dest_row_stride - 2], 80, 1);
+ EXPECT_NEAR(output[(test_row + 1) * dest_row_stride - 3], 60, 1);
+ EXPECT_NEAR(output[(test_row + 1) * dest_row_stride - 4], 40, 1);
+
+ SingleChannelConvolveY1D(&input[0], src_row_stride, 1, kChannelCount,
+ filter, SkISize::Make(kImgWidth, kImgHeight),
+ &output[0], dest_row_stride, 0, 1, false);
+
+ int test_column = 42;
+ EXPECT_NEAR(output[test_column], 100, 1);
+ EXPECT_NEAR(output[test_column + dest_row_stride], 80, 1);
+ EXPECT_NEAR(output[test_column + dest_row_stride * 2], 60, 1);
+ EXPECT_NEAR(output[test_column + dest_row_stride * 3], 40, 1);
+
+ EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 1)], 100, 1);
+ EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 2)], 80, 1);
+ EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 3)], 60, 1);
+ EXPECT_NEAR(output[test_column + dest_row_stride * (kImgHeight - 4)], 40, 1);
+}
+
+TEST(Convolver, SetUpGaussianConvolutionFilter) {
+ ConvolutionFilter1D smoothing_filter;
+ ConvolutionFilter1D gradient_filter;
+ SetUpGaussianConvolutionKernel(&smoothing_filter, 4.5f, false);
+ SetUpGaussianConvolutionKernel(&gradient_filter, 3.0f, true);
+
+ int specified_filter_length;
+ int filter_offset;
+ int filter_length;
+
+ const ConvolutionFilter1D::Fixed* smoothing_kernel =
+ smoothing_filter.GetSingleFilter(
+ &specified_filter_length, &filter_offset, &filter_length);
+ EXPECT_TRUE(smoothing_kernel);
+ std::vector<float> fp_smoothing_kernel(filter_length);
+ std::transform(smoothing_kernel,
+ smoothing_kernel + filter_length,
+ fp_smoothing_kernel.begin(),
+ ConvolutionFilter1D::FixedToFloat);
+ // Should sum-up to 1 (nearly), and all values whould be in ]0, 1[.
+ EXPECT_NEAR(std::accumulate(
+ fp_smoothing_kernel.begin(), fp_smoothing_kernel.end(), 0.0f),
+ 1.0f, 0.01f);
+ EXPECT_GT(*std::min_element(fp_smoothing_kernel.begin(),
+ fp_smoothing_kernel.end()), 0.0f);
+ EXPECT_LT(*std::max_element(fp_smoothing_kernel.begin(),
+ fp_smoothing_kernel.end()), 1.0f);
+
+ const ConvolutionFilter1D::Fixed* gradient_kernel =
+ gradient_filter.GetSingleFilter(
+ &specified_filter_length, &filter_offset, &filter_length);
+ EXPECT_TRUE(gradient_kernel);
+ std::vector<float> fp_gradient_kernel(filter_length);
+ std::transform(gradient_kernel,
+ gradient_kernel + filter_length,
+ fp_gradient_kernel.begin(),
+ ConvolutionFilter1D::FixedToFloat);
+ // Should sum-up to 0, and all values whould be in ]-1.5, 1.5[.
+ EXPECT_NEAR(std::accumulate(
+ fp_gradient_kernel.begin(), fp_gradient_kernel.end(), 0.0f),
+ 0.0f, 0.01f);
+ EXPECT_GT(*std::min_element(fp_gradient_kernel.begin(),
+ fp_gradient_kernel.end()), -1.5f);
+ EXPECT_LT(*std::min_element(fp_gradient_kernel.begin(),
+ fp_gradient_kernel.end()), 0.0f);
+ EXPECT_LT(*std::max_element(fp_gradient_kernel.begin(),
+ fp_gradient_kernel.end()), 1.5f);
+ EXPECT_GT(*std::max_element(fp_gradient_kernel.begin(),
+ fp_gradient_kernel.end()), 0.0f);
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/00_pc_clean.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/00_pc_clean.png
new file mode 100644
index 00000000000..a5435f278bc
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/00_pc_clean.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/00_vc_clean.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/00_vc_clean.png
new file mode 100644
index 00000000000..a5435f278bc
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/00_vc_clean.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png
new file mode 100644
index 00000000000..a5435f278bc
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png
new file mode 100644
index 00000000000..a5435f278bc
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png
new file mode 100644
index 00000000000..c21fdf1c577
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png
new file mode 100644
index 00000000000..c21fdf1c577
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png
new file mode 100644
index 00000000000..dfc46a80d8a
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png
new file mode 100644
index 00000000000..dfc46a80d8a
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png
new file mode 100644
index 00000000000..dfc46a80d8a
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png
new file mode 100644
index 00000000000..dfc46a80d8a
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png
new file mode 100644
index 00000000000..69cc6dce635
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png
new file mode 100644
index 00000000000..69cc6dce635
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png
new file mode 100644
index 00000000000..9cbff6e164f
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png
new file mode 100644
index 00000000000..9cbff6e164f
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png
new file mode 100644
index 00000000000..bbdfc36cb56
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png
new file mode 100644
index 00000000000..bbdfc36cb56
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png
new file mode 100644
index 00000000000..9dc35f051e9
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png
new file mode 100644
index 00000000000..9dc35f051e9
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/bitmaps/00_pc_opaque.png b/chromium/skia/ext/data/vectorcanvastest/bitmaps/00_pc_opaque.png
new file mode 100644
index 00000000000..812b1ca293a
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/bitmaps/00_pc_opaque.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/bitmaps/00_vc_opaque.png b/chromium/skia/ext/data/vectorcanvastest/bitmaps/00_vc_opaque.png
new file mode 100644
index 00000000000..812b1ca293a
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/bitmaps/00_vc_opaque.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/bitmaps/01_pc_alpha.png b/chromium/skia/ext/data/vectorcanvastest/bitmaps/01_pc_alpha.png
new file mode 100644
index 00000000000..1d1342bbaa8
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/bitmaps/01_pc_alpha.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/bitmaps/01_vc_alpha.png b/chromium/skia/ext/data/vectorcanvastest/bitmaps/01_vc_alpha.png
new file mode 100644
index 00000000000..1d1342bbaa8
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/bitmaps/01_vc_alpha.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/bitmaps/bitmap_alpha.png b/chromium/skia/ext/data/vectorcanvastest/bitmaps/bitmap_alpha.png
new file mode 100644
index 00000000000..a19d09d06c7
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/bitmaps/bitmap_alpha.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/bitmaps/bitmap_opaque.png b/chromium/skia/ext/data/vectorcanvastest/bitmaps/bitmap_opaque.png
new file mode 100644
index 00000000000..3560d270f59
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/bitmaps/bitmap_opaque.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/00_pc_circle_stroke.png b/chromium/skia/ext/data/vectorcanvastest/circles/00_pc_circle_stroke.png
new file mode 100644
index 00000000000..896631b3231
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/00_pc_circle_stroke.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/00_vc_circle_stroke.png b/chromium/skia/ext/data/vectorcanvastest/circles/00_vc_circle_stroke.png
new file mode 100644
index 00000000000..c265be341fe
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/00_vc_circle_stroke.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/01_pc_circle_fill.png b/chromium/skia/ext/data/vectorcanvastest/circles/01_pc_circle_fill.png
new file mode 100644
index 00000000000..92b647d046a
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/01_pc_circle_fill.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/01_vc_circle_fill.png b/chromium/skia/ext/data/vectorcanvastest/circles/01_vc_circle_fill.png
new file mode 100644
index 00000000000..f5270cea5a7
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/01_vc_circle_fill.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/02_pc_circle_over_strike.png b/chromium/skia/ext/data/vectorcanvastest/circles/02_pc_circle_over_strike.png
new file mode 100644
index 00000000000..64ae06ab9d8
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/02_pc_circle_over_strike.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/02_vc_circle_over_strike.png b/chromium/skia/ext/data/vectorcanvastest/circles/02_vc_circle_over_strike.png
new file mode 100644
index 00000000000..4d3d1b069ee
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/02_vc_circle_over_strike.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png b/chromium/skia/ext/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png
new file mode 100644
index 00000000000..6aeeb49b083
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png b/chromium/skia/ext/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png
new file mode 100644
index 00000000000..f073a3e8c8d
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/04_pc_mixed_stroke.png b/chromium/skia/ext/data/vectorcanvastest/circles/04_pc_mixed_stroke.png
new file mode 100644
index 00000000000..e4a044fa26b
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/04_pc_mixed_stroke.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/circles/04_vc_mixed_stroke.png b/chromium/skia/ext/data/vectorcanvastest/circles/04_vc_mixed_stroke.png
new file mode 100644
index 00000000000..efd9e3abdfb
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/circles/04_vc_mixed_stroke.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingclean/00_pc_clipped.png b/chromium/skia/ext/data/vectorcanvastest/clippingclean/00_pc_clipped.png
new file mode 100644
index 00000000000..14ff949d624
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingclean/00_pc_clipped.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingclean/00_vc_clipped.png b/chromium/skia/ext/data/vectorcanvastest/clippingclean/00_vc_clipped.png
new file mode 100644
index 00000000000..14ff949d624
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingclean/00_vc_clipped.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingclean/01_pc_unclipped.png b/chromium/skia/ext/data/vectorcanvastest/clippingclean/01_pc_unclipped.png
new file mode 100644
index 00000000000..436f9a5b7c3
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingclean/01_pc_unclipped.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingclean/01_vc_unclipped.png b/chromium/skia/ext/data/vectorcanvastest/clippingclean/01_vc_unclipped.png
new file mode 100644
index 00000000000..436f9a5b7c3
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingclean/01_vc_unclipped.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingcombined/00_pc_combined.png b/chromium/skia/ext/data/vectorcanvastest/clippingcombined/00_pc_combined.png
new file mode 100644
index 00000000000..14ff949d624
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingcombined/00_pc_combined.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingcombined/00_vc_combined.png b/chromium/skia/ext/data/vectorcanvastest/clippingcombined/00_vc_combined.png
new file mode 100644
index 00000000000..14ff949d624
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingcombined/00_vc_combined.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingintersect/00_pc_intersect.png b/chromium/skia/ext/data/vectorcanvastest/clippingintersect/00_pc_intersect.png
new file mode 100644
index 00000000000..1285dac0dba
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingintersect/00_pc_intersect.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingintersect/00_vc_intersect.png b/chromium/skia/ext/data/vectorcanvastest/clippingintersect/00_vc_intersect.png
new file mode 100644
index 00000000000..1285dac0dba
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingintersect/00_vc_intersect.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingpath/00_pc_path.png b/chromium/skia/ext/data/vectorcanvastest/clippingpath/00_pc_path.png
new file mode 100644
index 00000000000..8807649bb93
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingpath/00_pc_path.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingpath/00_vc_path.png b/chromium/skia/ext/data/vectorcanvastest/clippingpath/00_vc_path.png
new file mode 100644
index 00000000000..8807649bb93
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingpath/00_vc_path.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingrect/00_pc_rect.png b/chromium/skia/ext/data/vectorcanvastest/clippingrect/00_pc_rect.png
new file mode 100644
index 00000000000..9c365e13dd5
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingrect/00_pc_rect.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/clippingrect/00_vc_rect.png b/chromium/skia/ext/data/vectorcanvastest/clippingrect/00_vc_rect.png
new file mode 100644
index 00000000000..9c365e13dd5
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/clippingrect/00_vc_rect.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/diagonallines/00_pc_nw-se.png b/chromium/skia/ext/data/vectorcanvastest/diagonallines/00_pc_nw-se.png
new file mode 100644
index 00000000000..5736c3503ad
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/diagonallines/00_pc_nw-se.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/diagonallines/00_vc_nw-se.png b/chromium/skia/ext/data/vectorcanvastest/diagonallines/00_vc_nw-se.png
new file mode 100644
index 00000000000..5736c3503ad
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/diagonallines/00_vc_nw-se.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png b/chromium/skia/ext/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png
new file mode 100644
index 00000000000..bfffd8a52a6
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png b/chromium/skia/ext/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png
new file mode 100644
index 00000000000..ae6b7537fc1
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png b/chromium/skia/ext/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png
new file mode 100644
index 00000000000..75acdad042a
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png b/chromium/skia/ext/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png
new file mode 100644
index 00000000000..86a67992afe
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/diagonallines/03_pc_se-nw.png b/chromium/skia/ext/data/vectorcanvastest/diagonallines/03_pc_se-nw.png
new file mode 100644
index 00000000000..50502cc1b8c
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/diagonallines/03_pc_se-nw.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/diagonallines/03_vc_se-nw.png b/chromium/skia/ext/data/vectorcanvastest/diagonallines/03_vc_se-nw.png
new file mode 100644
index 00000000000..362f6e727e7
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/diagonallines/03_vc_se-nw.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/lineorientation/00_pc_horizontal.png b/chromium/skia/ext/data/vectorcanvastest/lineorientation/00_pc_horizontal.png
new file mode 100644
index 00000000000..7bcd99885a8
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/lineorientation/00_pc_horizontal.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/lineorientation/00_vc_horizontal.png b/chromium/skia/ext/data/vectorcanvastest/lineorientation/00_vc_horizontal.png
new file mode 100644
index 00000000000..46c9b0abe18
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/lineorientation/00_vc_horizontal.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/lineorientation/01_pc_vertical.png b/chromium/skia/ext/data/vectorcanvastest/lineorientation/01_pc_vertical.png
new file mode 100644
index 00000000000..09f41db50fa
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/lineorientation/01_pc_vertical.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/lineorientation/01_vc_vertical.png b/chromium/skia/ext/data/vectorcanvastest/lineorientation/01_vc_vertical.png
new file mode 100644
index 00000000000..7f5f1f7f156
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/lineorientation/01_vc_vertical.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png b/chromium/skia/ext/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png
new file mode 100644
index 00000000000..5966df640eb
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png b/chromium/skia/ext/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png
new file mode 100644
index 00000000000..e43a844985d
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png b/chromium/skia/ext/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png
new file mode 100644
index 00000000000..9ac482558f8
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png b/chromium/skia/ext/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png
new file mode 100644
index 00000000000..d9e033abf59
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/matrix/00_pc_translate1.png b/chromium/skia/ext/data/vectorcanvastest/matrix/00_pc_translate1.png
new file mode 100644
index 00000000000..fe27cb31581
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/matrix/00_pc_translate1.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/matrix/00_vc_translate1.png b/chromium/skia/ext/data/vectorcanvastest/matrix/00_vc_translate1.png
new file mode 100644
index 00000000000..fe27cb31581
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/matrix/00_vc_translate1.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/matrix/01_pc_translate2.png b/chromium/skia/ext/data/vectorcanvastest/matrix/01_pc_translate2.png
new file mode 100644
index 00000000000..406bf5776e1
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/matrix/01_pc_translate2.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/matrix/01_vc_translate2.png b/chromium/skia/ext/data/vectorcanvastest/matrix/01_vc_translate2.png
new file mode 100644
index 00000000000..406bf5776e1
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/matrix/01_vc_translate2.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/matrix/02_pc_scale.png b/chromium/skia/ext/data/vectorcanvastest/matrix/02_pc_scale.png
new file mode 100644
index 00000000000..9e94fb0c6f7
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/matrix/02_pc_scale.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/matrix/02_vc_scale.png b/chromium/skia/ext/data/vectorcanvastest/matrix/02_vc_scale.png
new file mode 100644
index 00000000000..fde62aadcb6
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/matrix/02_vc_scale.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/matrix/03_pc_rotate.png b/chromium/skia/ext/data/vectorcanvastest/matrix/03_pc_rotate.png
new file mode 100644
index 00000000000..7a43a2ad665
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/matrix/03_pc_rotate.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/matrix/03_vc_rotate.png b/chromium/skia/ext/data/vectorcanvastest/matrix/03_vc_rotate.png
new file mode 100644
index 00000000000..7a22b7f14d7
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/matrix/03_vc_rotate.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/patheffects/00_pc_dash_line.png b/chromium/skia/ext/data/vectorcanvastest/patheffects/00_pc_dash_line.png
new file mode 100644
index 00000000000..e08d3e2a3f5
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/patheffects/00_pc_dash_line.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/patheffects/00_vc_dash_line.png b/chromium/skia/ext/data/vectorcanvastest/patheffects/00_vc_dash_line.png
new file mode 100644
index 00000000000..e08d3e2a3f5
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/patheffects/00_vc_dash_line.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/patheffects/01_pc_dash_path.png b/chromium/skia/ext/data/vectorcanvastest/patheffects/01_pc_dash_path.png
new file mode 100644
index 00000000000..3a301354219
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/patheffects/01_pc_dash_path.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/patheffects/01_vc_dash_path.png b/chromium/skia/ext/data/vectorcanvastest/patheffects/01_vc_dash_path.png
new file mode 100644
index 00000000000..7868b9a4ed5
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/patheffects/01_vc_dash_path.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/patheffects/02_pc_dash_rect.png b/chromium/skia/ext/data/vectorcanvastest/patheffects/02_pc_dash_rect.png
new file mode 100644
index 00000000000..04f2cebae2c
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/patheffects/02_pc_dash_rect.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/patheffects/02_vc_dash_rect.png b/chromium/skia/ext/data/vectorcanvastest/patheffects/02_vc_dash_rect.png
new file mode 100644
index 00000000000..5344eee2e86
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/patheffects/02_vc_dash_rect.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/patheffects/03_pc_circle.png b/chromium/skia/ext/data/vectorcanvastest/patheffects/03_pc_circle.png
new file mode 100644
index 00000000000..4c267ef765d
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/patheffects/03_pc_circle.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/patheffects/03_vc_circle.png b/chromium/skia/ext/data/vectorcanvastest/patheffects/03_vc_circle.png
new file mode 100644
index 00000000000..46ac35d05ba
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/patheffects/03_vc_circle.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png b/chromium/skia/ext/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png
new file mode 100644
index 00000000000..cefbf8700b3
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png b/chromium/skia/ext/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png
new file mode 100644
index 00000000000..cefbf8700b3
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png b/chromium/skia/ext/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png
new file mode 100644
index 00000000000..7bcd99885a8
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png
Binary files differ
diff --git a/chromium/skia/ext/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png b/chromium/skia/ext/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png
new file mode 100644
index 00000000000..46c9b0abe18
--- /dev/null
+++ b/chromium/skia/ext/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png
Binary files differ
diff --git a/chromium/skia/ext/google_logging.cc b/chromium/skia/ext/google_logging.cc
new file mode 100644
index 00000000000..673e23ad00c
--- /dev/null
+++ b/chromium/skia/ext/google_logging.cc
@@ -0,0 +1,25 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file provides integration with Google-style "base/logging.h" assertions
+// for Skia SkASSERT. If you don't want this, you can link with another file
+// that provides integration with the logging of your choice.
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+void SkDebugf_FileLine(const char* file, int line, bool fatal,
+ const char* format, ...) {
+ va_list ap;
+ va_start(ap, format);
+
+ std::string msg;
+ base::StringAppendV(&msg, format, ap);
+ va_end(ap);
+
+ logging::LogMessage(file, line,
+ fatal ? logging::LOG_FATAL : logging::LOG_INFO).stream()
+ << msg;
+}
diff --git a/chromium/skia/ext/image_operations.cc b/chromium/skia/ext/image_operations.cc
new file mode 100644
index 00000000000..c61da0cfa53
--- /dev/null
+++ b/chromium/skia/ext/image_operations.cc
@@ -0,0 +1,544 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define _USE_MATH_DEFINES
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+#include "skia/ext/image_operations.h"
+
+// TODO(pkasting): skia/ext should not depend on base/!
+#include "base/containers/stack_container.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/metrics/histogram.h"
+#include "base/time/time.h"
+#include "build/build_config.h"
+#include "skia/ext/convolver.h"
+#include "third_party/skia/include/core/SkColorPriv.h"
+#include "third_party/skia/include/core/SkFontHost.h"
+#include "third_party/skia/include/core/SkRect.h"
+
+namespace skia {
+
+namespace {
+
+// Returns the ceiling/floor as an integer.
+inline int CeilInt(float val) {
+ return static_cast<int>(ceil(val));
+}
+inline int FloorInt(float val) {
+ return static_cast<int>(floor(val));
+}
+
+// Filter function computation -------------------------------------------------
+
+// Evaluates the box filter, which goes from -0.5 to +0.5.
+float EvalBox(float x) {
+ return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
+}
+
+// Evaluates the Lanczos filter of the given filter size window for the given
+// position.
+//
+// |filter_size| is the width of the filter (the "window"), outside of which
+// the value of the function is 0. Inside of the window, the value is the
+// normalized sinc function:
+// lanczos(x) = sinc(x) * sinc(x / filter_size);
+// where
+// sinc(x) = sin(pi*x) / (pi*x);
+float EvalLanczos(int filter_size, float x) {
+ if (x <= -filter_size || x >= filter_size)
+ return 0.0f; // Outside of the window.
+ if (x > -std::numeric_limits<float>::epsilon() &&
+ x < std::numeric_limits<float>::epsilon())
+ return 1.0f; // Special case the discontinuity at the origin.
+ float xpi = x * static_cast<float>(M_PI);
+ return (sin(xpi) / xpi) * // sinc(x)
+ sin(xpi / filter_size) / (xpi / filter_size); // sinc(x/filter_size)
+}
+
+// Evaluates the Hamming filter of the given filter size window for the given
+// position.
+//
+// The filter covers [-filter_size, +filter_size]. Outside of this window
+// the value of the function is 0. Inside of the window, the value is sinus
+// cardinal multiplied by a recentered Hamming function. The traditional
+// Hamming formula for a window of size N and n ranging in [0, N-1] is:
+// hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
+// In our case we want the function centered for x == 0 and at its minimum
+// on both ends of the window (x == +/- filter_size), hence the adjusted
+// formula:
+// hamming(x) = (0.54 -
+// 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
+// = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
+// = 0.54 + 0.46 * cos(pi * x / filter_size)
+float EvalHamming(int filter_size, float x) {
+ if (x <= -filter_size || x >= filter_size)
+ return 0.0f; // Outside of the window.
+ if (x > -std::numeric_limits<float>::epsilon() &&
+ x < std::numeric_limits<float>::epsilon())
+ return 1.0f; // Special case the sinc discontinuity at the origin.
+ const float xpi = x * static_cast<float>(M_PI);
+
+ return ((sin(xpi) / xpi) * // sinc(x)
+ (0.54f + 0.46f * cos(xpi / filter_size))); // hamming(x)
+}
+
+// ResizeFilter ----------------------------------------------------------------
+
+// Encapsulates computation and storage of the filters required for one complete
+// resize operation.
+class ResizeFilter {
+ public:
+ ResizeFilter(ImageOperations::ResizeMethod method,
+ int src_full_width, int src_full_height,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset);
+
+ // Returns the filled filter values.
+ const ConvolutionFilter1D& x_filter() { return x_filter_; }
+ const ConvolutionFilter1D& y_filter() { return y_filter_; }
+
+ private:
+ // Returns the number of pixels that the filer spans, in filter space (the
+ // destination image).
+ float GetFilterSupport(float scale) {
+ switch (method_) {
+ case ImageOperations::RESIZE_BOX:
+ // The box filter just scales with the image scaling.
+ return 0.5f; // Only want one side of the filter = /2.
+ case ImageOperations::RESIZE_HAMMING1:
+ // The Hamming filter takes as much space in the source image in
+ // each direction as the size of the window = 1 for Hamming1.
+ return 1.0f;
+ case ImageOperations::RESIZE_LANCZOS2:
+ // The Lanczos filter takes as much space in the source image in
+ // each direction as the size of the window = 2 for Lanczos2.
+ return 2.0f;
+ case ImageOperations::RESIZE_LANCZOS3:
+ // The Lanczos filter takes as much space in the source image in
+ // each direction as the size of the window = 3 for Lanczos3.
+ return 3.0f;
+ default:
+ NOTREACHED();
+ return 1.0f;
+ }
+ }
+
+ // Computes one set of filters either horizontally or vertically. The caller
+ // will specify the "min" and "max" rather than the bottom/top and
+ // right/bottom so that the same code can be re-used in each dimension.
+ //
+ // |src_depend_lo| and |src_depend_size| gives the range for the source
+ // depend rectangle (horizontally or vertically at the caller's discretion
+ // -- see above for what this means).
+ //
+ // Likewise, the range of destination values to compute and the scale factor
+ // for the transform is also specified.
+ void ComputeFilters(int src_size,
+ int dest_subset_lo, int dest_subset_size,
+ float scale,
+ ConvolutionFilter1D* output);
+
+ // Computes the filter value given the coordinate in filter space.
+ inline float ComputeFilter(float pos) {
+ switch (method_) {
+ case ImageOperations::RESIZE_BOX:
+ return EvalBox(pos);
+ case ImageOperations::RESIZE_HAMMING1:
+ return EvalHamming(1, pos);
+ case ImageOperations::RESIZE_LANCZOS2:
+ return EvalLanczos(2, pos);
+ case ImageOperations::RESIZE_LANCZOS3:
+ return EvalLanczos(3, pos);
+ default:
+ NOTREACHED();
+ return 0;
+ }
+ }
+
+ ImageOperations::ResizeMethod method_;
+
+ // Size of the filter support on one side only in the destination space.
+ // See GetFilterSupport.
+ float x_filter_support_;
+ float y_filter_support_;
+
+ // Subset of scaled destination bitmap to compute.
+ SkIRect out_bounds_;
+
+ ConvolutionFilter1D x_filter_;
+ ConvolutionFilter1D y_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ResizeFilter);
+};
+
+ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
+ int src_full_width, int src_full_height,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset)
+ : method_(method),
+ out_bounds_(dest_subset) {
+ // method_ will only ever refer to an "algorithm method".
+ SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
+
+ float scale_x = static_cast<float>(dest_width) /
+ static_cast<float>(src_full_width);
+ float scale_y = static_cast<float>(dest_height) /
+ static_cast<float>(src_full_height);
+
+ ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(),
+ scale_x, &x_filter_);
+ ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(),
+ scale_y, &y_filter_);
+}
+
+// TODO(egouriou): Take advantage of periods in the convolution.
+// Practical resizing filters are periodic outside of the border area.
+// For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the
+// source become p pixels in the destination) will have a period of p.
+// A nice consequence is a period of 1 when downscaling by an integral
+// factor. Downscaling from typical display resolutions is also bound
+// to produce interesting periods as those are chosen to have multiple
+// small factors.
+// Small periods reduce computational load and improve cache usage if
+// the coefficients can be shared. For periods of 1 we can consider
+// loading the factors only once outside the borders.
+void ResizeFilter::ComputeFilters(int src_size,
+ int dest_subset_lo, int dest_subset_size,
+ float scale,
+ ConvolutionFilter1D* output) {
+ int dest_subset_hi = dest_subset_lo + dest_subset_size; // [lo, hi)
+
+ // When we're doing a magnification, the scale will be larger than one. This
+ // means the destination pixels are much smaller than the source pixels, and
+ // that the range covered by the filter won't necessarily cover any source
+ // pixel boundaries. Therefore, we use these clamped values (max of 1) for
+ // some computations.
+ float clamped_scale = std::min(1.0f, scale);
+
+ // This is how many source pixels from the center we need to count
+ // to support the filtering function.
+ float src_support = GetFilterSupport(clamped_scale) / clamped_scale;
+
+ // Speed up the divisions below by turning them into multiplies.
+ float inv_scale = 1.0f / scale;
+
+ base::StackVector<float, 64> filter_values;
+ base::StackVector<int16, 64> fixed_filter_values;
+
+ // Loop over all pixels in the output range. We will generate one set of
+ // filter values for each one. Those values will tell us how to blend the
+ // source pixels to compute the destination pixel.
+ for (int dest_subset_i = dest_subset_lo; dest_subset_i < dest_subset_hi;
+ dest_subset_i++) {
+ // Reset the arrays. We don't declare them inside so they can re-use the
+ // same malloc-ed buffer.
+ filter_values->clear();
+ fixed_filter_values->clear();
+
+ // This is the pixel in the source directly under the pixel in the dest.
+ // Note that we base computations on the "center" of the pixels. To see
+ // why, observe that the destination pixel at coordinates (0, 0) in a 5.0x
+ // downscale should "cover" the pixels around the pixel with *its center*
+ // at coordinates (2.5, 2.5) in the source, not those around (0, 0).
+ // Hence we need to scale coordinates (0.5, 0.5), not (0, 0).
+ float src_pixel = (static_cast<float>(dest_subset_i) + 0.5f) * inv_scale;
+
+ // Compute the (inclusive) range of source pixels the filter covers.
+ int src_begin = std::max(0, FloorInt(src_pixel - src_support));
+ int src_end = std::min(src_size - 1, CeilInt(src_pixel + src_support));
+
+ // Compute the unnormalized filter value at each location of the source
+ // it covers.
+ float filter_sum = 0.0f; // Sub of the filter values for normalizing.
+ for (int cur_filter_pixel = src_begin; cur_filter_pixel <= src_end;
+ cur_filter_pixel++) {
+ // Distance from the center of the filter, this is the filter coordinate
+ // in source space. We also need to consider the center of the pixel
+ // when comparing distance against 'src_pixel'. In the 5x downscale
+ // example used above the distance from the center of the filter to
+ // the pixel with coordinates (2, 2) should be 0, because its center
+ // is at (2.5, 2.5).
+ float src_filter_dist =
+ ((static_cast<float>(cur_filter_pixel) + 0.5f) - src_pixel);
+
+ // Since the filter really exists in dest space, map it there.
+ float dest_filter_dist = src_filter_dist * clamped_scale;
+
+ // Compute the filter value at that location.
+ float filter_value = ComputeFilter(dest_filter_dist);
+ filter_values->push_back(filter_value);
+
+ filter_sum += filter_value;
+ }
+ DCHECK(!filter_values->empty()) << "We should always get a filter!";
+
+ // The filter must be normalized so that we don't affect the brightness of
+ // the image. Convert to normalized fixed point.
+ int16 fixed_sum = 0;
+ for (size_t i = 0; i < filter_values->size(); i++) {
+ int16 cur_fixed = output->FloatToFixed(filter_values[i] / filter_sum);
+ fixed_sum += cur_fixed;
+ fixed_filter_values->push_back(cur_fixed);
+ }
+
+ // The conversion to fixed point will leave some rounding errors, which
+ // we add back in to avoid affecting the brightness of the image. We
+ // arbitrarily add this to the center of the filter array (this won't always
+ // be the center of the filter function since it could get clipped on the
+ // edges, but it doesn't matter enough to worry about that case).
+ int16 leftovers = output->FloatToFixed(1.0f) - fixed_sum;
+ fixed_filter_values[fixed_filter_values->size() / 2] += leftovers;
+
+ // Now it's ready to go.
+ output->AddFilter(src_begin, &fixed_filter_values[0],
+ static_cast<int>(fixed_filter_values->size()));
+ }
+
+ output->PaddingForSIMD();
+}
+
+ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
+ ImageOperations::ResizeMethod method) {
+ // Convert any "Quality Method" into an "Algorithm Method"
+ if (method >= ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD &&
+ method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD) {
+ return method;
+ }
+ // The call to ImageOperationsGtv::Resize() above took care of
+ // GPU-acceleration in the cases where it is possible. So now we just
+ // pick the appropriate software method for each resize quality.
+ switch (method) {
+ // Users of RESIZE_GOOD are willing to trade a lot of quality to
+ // get speed, allowing the use of linear resampling to get hardware
+ // acceleration (SRB). Hence any of our "good" software filters
+ // will be acceptable, and we use the fastest one, Hamming-1.
+ case ImageOperations::RESIZE_GOOD:
+ // Users of RESIZE_BETTER are willing to trade some quality in order
+ // to improve performance, but are guaranteed not to devolve to a linear
+ // resampling. In visual tests we see that Hamming-1 is not as good as
+ // Lanczos-2, however it is about 40% faster and Lanczos-2 itself is
+ // about 30% faster than Lanczos-3. The use of Hamming-1 has been deemed
+ // an acceptable trade-off between quality and speed.
+ case ImageOperations::RESIZE_BETTER:
+ return ImageOperations::RESIZE_HAMMING1;
+ default:
+ return ImageOperations::RESIZE_LANCZOS3;
+ }
+}
+
+} // namespace
+
+// Resize ----------------------------------------------------------------------
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ SkBitmap::Allocator* allocator) {
+ if (method == ImageOperations::RESIZE_SUBPIXEL) {
+ return ResizeSubpixel(source, dest_width, dest_height,
+ dest_subset, allocator);
+ } else {
+ return ResizeBasic(source, method, dest_width, dest_height, dest_subset,
+ allocator);
+ }
+}
+
+// static
+SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ SkBitmap::Allocator* allocator) {
+ TRACE_EVENT2("skia", "ImageOperations::ResizeSubpixel",
+ "src_pixels", source.width()*source.height(),
+ "dst_pixels", dest_width*dest_height);
+ // Currently only works on Linux/BSD because these are the only platforms
+ // where SkFontHost::GetSubpixelOrder is defined.
+#if defined(OS_LINUX) && !defined(GTV)
+ // Understand the display.
+ const SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
+ const SkFontHost::LCDOrientation orientation =
+ SkFontHost::GetSubpixelOrientation();
+
+ // Decide on which dimension, if any, to deploy subpixel rendering.
+ int w = 1;
+ int h = 1;
+ switch (orientation) {
+ case SkFontHost::kHorizontal_LCDOrientation:
+ w = dest_width < source.width() ? 3 : 1;
+ break;
+ case SkFontHost::kVertical_LCDOrientation:
+ h = dest_height < source.height() ? 3 : 1;
+ break;
+ }
+
+ // Resize the image.
+ const int width = dest_width * w;
+ const int height = dest_height * h;
+ SkIRect subset = { dest_subset.fLeft, dest_subset.fTop,
+ dest_subset.fLeft + dest_subset.width() * w,
+ dest_subset.fTop + dest_subset.height() * h };
+ SkBitmap img = ResizeBasic(source, ImageOperations::RESIZE_LANCZOS3, width,
+ height, subset, allocator);
+ const int row_words = img.rowBytes() / 4;
+ if (w == 1 && h == 1)
+ return img;
+
+ // Render into subpixels.
+ SkBitmap result;
+ result.setConfig(SkBitmap::kARGB_8888_Config, dest_subset.width(),
+ dest_subset.height());
+ result.allocPixels(allocator, NULL);
+ if (!result.readyToDraw())
+ return img;
+
+ SkAutoLockPixels locker(img);
+ if (!img.readyToDraw())
+ return img;
+
+ uint32* src_row = img.getAddr32(0, 0);
+ uint32* dst_row = result.getAddr32(0, 0);
+ for (int y = 0; y < dest_subset.height(); y++) {
+ uint32* src = src_row;
+ uint32* dst = dst_row;
+ for (int x = 0; x < dest_subset.width(); x++, src += w, dst++) {
+ uint8 r = 0, g = 0, b = 0, a = 0;
+ switch (order) {
+ case SkFontHost::kRGB_LCDOrder:
+ switch (orientation) {
+ case SkFontHost::kHorizontal_LCDOrientation:
+ r = SkGetPackedR32(src[0]);
+ g = SkGetPackedG32(src[1]);
+ b = SkGetPackedB32(src[2]);
+ a = SkGetPackedA32(src[1]);
+ break;
+ case SkFontHost::kVertical_LCDOrientation:
+ r = SkGetPackedR32(src[0 * row_words]);
+ g = SkGetPackedG32(src[1 * row_words]);
+ b = SkGetPackedB32(src[2 * row_words]);
+ a = SkGetPackedA32(src[1 * row_words]);
+ break;
+ }
+ break;
+ case SkFontHost::kBGR_LCDOrder:
+ switch (orientation) {
+ case SkFontHost::kHorizontal_LCDOrientation:
+ b = SkGetPackedB32(src[0]);
+ g = SkGetPackedG32(src[1]);
+ r = SkGetPackedR32(src[2]);
+ a = SkGetPackedA32(src[1]);
+ break;
+ case SkFontHost::kVertical_LCDOrientation:
+ b = SkGetPackedB32(src[0 * row_words]);
+ g = SkGetPackedG32(src[1 * row_words]);
+ r = SkGetPackedR32(src[2 * row_words]);
+ a = SkGetPackedA32(src[1 * row_words]);
+ break;
+ }
+ break;
+ case SkFontHost::kNONE_LCDOrder:
+ NOTREACHED();
+ }
+ // Premultiplied alpha is very fragile.
+ a = a > r ? a : r;
+ a = a > g ? a : g;
+ a = a > b ? a : b;
+ *dst = SkPackARGB32(a, r, g, b);
+ }
+ src_row += h * row_words;
+ dst_row += result.rowBytes() / 4;
+ }
+ result.setIsOpaque(img.isOpaque());
+ return result;
+#else
+ return SkBitmap();
+#endif // OS_POSIX && !OS_MACOSX && !defined(OS_ANDROID)
+}
+
+// static
+SkBitmap ImageOperations::ResizeBasic(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ SkBitmap::Allocator* allocator) {
+ TRACE_EVENT2("skia", "ImageOperations::ResizeBasic",
+ "src_pixels", source.width()*source.height(),
+ "dst_pixels", dest_width*dest_height);
+ // Ensure that the ResizeMethod enumeration is sound.
+ SkASSERT(((RESIZE_FIRST_QUALITY_METHOD <= method) &&
+ (method <= RESIZE_LAST_QUALITY_METHOD)) ||
+ ((RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= RESIZE_LAST_ALGORITHM_METHOD)));
+
+ // Time how long this takes to see if it's a problem for users.
+ base::TimeTicks resize_start = base::TimeTicks::Now();
+
+ SkIRect dest = { 0, 0, dest_width, dest_height };
+ DCHECK(dest.contains(dest_subset)) <<
+ "The supplied subset does not fall within the destination image.";
+
+ // If the size of source or destination is 0, i.e. 0x0, 0xN or Nx0, just
+ // return empty.
+ if (source.width() < 1 || source.height() < 1 ||
+ dest_width < 1 || dest_height < 1)
+ return SkBitmap();
+
+ method = ResizeMethodToAlgorithmMethod(method);
+ // Check that we deal with an "algorithm methods" from this point onward.
+ SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+ (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
+
+ SkAutoLockPixels locker(source);
+ if (!source.readyToDraw() || source.config() != SkBitmap::kARGB_8888_Config)
+ return SkBitmap();
+
+ ResizeFilter filter(method, source.width(), source.height(),
+ dest_width, dest_height, dest_subset);
+
+ // Get a source bitmap encompassing this touched area. We construct the
+ // offsets and row strides such that it looks like a new bitmap, while
+ // referring to the old data.
+ const uint8* source_subset =
+ reinterpret_cast<const uint8*>(source.getPixels());
+
+ // Convolve into the result.
+ SkBitmap result;
+ result.setConfig(SkBitmap::kARGB_8888_Config,
+ dest_subset.width(), dest_subset.height());
+ result.allocPixels(allocator, NULL);
+ if (!result.readyToDraw())
+ return SkBitmap();
+
+ BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
+ !source.isOpaque(), filter.x_filter(), filter.y_filter(),
+ static_cast<int>(result.rowBytes()),
+ static_cast<unsigned char*>(result.getPixels()),
+ true);
+
+ // Preserve the "opaque" flag for use as an optimization later.
+ result.setIsOpaque(source.isOpaque());
+
+ base::TimeDelta delta = base::TimeTicks::Now() - resize_start;
+ UMA_HISTOGRAM_TIMES("Image.ResampleMS", delta);
+
+ return result;
+}
+
+// static
+SkBitmap ImageOperations::Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ SkBitmap::Allocator* allocator) {
+ SkIRect dest_subset = { 0, 0, dest_width, dest_height };
+ return Resize(source, method, dest_width, dest_height, dest_subset,
+ allocator);
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/image_operations.h b/chromium/skia/ext/image_operations.h
new file mode 100644
index 00000000000..eff89123c6b
--- /dev/null
+++ b/chromium/skia/ext/image_operations.h
@@ -0,0 +1,133 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_IMAGE_OPERATIONS_H_
+#define SKIA_EXT_IMAGE_OPERATIONS_H_
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+struct SkIRect;
+
+namespace skia {
+
+class SK_API ImageOperations {
+ public:
+ enum ResizeMethod {
+ //
+ // Quality Methods
+ //
+ // Those enumeration values express a desired quality/speed tradeoff.
+ // They are translated into an algorithm-specific method that depends
+ // on the capabilities (CPU, GPU) of the underlying platform.
+ // It is possible for all three methods to be mapped to the same
+ // algorithm on a given platform.
+
+ // Good quality resizing. Fastest resizing with acceptable visual quality.
+ // This is typically intended for use during interactive layouts
+ // where slower platforms may want to trade image quality for large
+ // increase in resizing performance.
+ //
+ // For example the resizing implementation may devolve to linear
+ // filtering if this enables GPU acceleration to be used.
+ //
+ // Note that the underlying resizing method may be determined
+ // on the fly based on the parameters for a given resize call.
+ // For example an implementation using a GPU-based linear filter
+ // in the common case may still use a higher-quality software-based
+ // filter in cases where using the GPU would actually be slower - due
+ // to too much latency - or impossible - due to image format or size
+ // constraints.
+ RESIZE_GOOD,
+
+ // Medium quality resizing. Close to high quality resizing (better
+ // than linear interpolation) with potentially some quality being
+ // traded-off for additional speed compared to RESIZE_BEST.
+ //
+ // This is intended, for example, for generation of large thumbnails
+ // (hundreds of pixels in each dimension) from large sources, where
+ // a linear filter would produce too many artifacts but where
+ // a RESIZE_HIGH might be too costly time-wise.
+ RESIZE_BETTER,
+
+ // High quality resizing. The algorithm is picked to favor image quality.
+ RESIZE_BEST,
+
+ //
+ // Algorithm-specific enumerations
+ //
+
+ // Box filter. This is a weighted average of all of the pixels touching
+ // the destination pixel. For enlargement, this is nearest neighbor.
+ //
+ // You probably don't want this, it is here for testing since it is easy to
+ // compute. Use RESIZE_LANCZOS3 instead.
+ RESIZE_BOX,
+
+ // 1-cycle Hamming filter. This is tall is the middle and falls off towards
+ // the window edges but without going to 0. This is about 40% faster than
+ // a 2-cycle Lanczos.
+ RESIZE_HAMMING1,
+
+ // 2-cycle Lanczos filter. This is tall in the middle, goes negative on
+ // each side, then returns to zero. Does not provide as good a frequency
+ // response as a 3-cycle Lanczos but is roughly 30% faster.
+ RESIZE_LANCZOS2,
+
+ // 3-cycle Lanczos filter. This is tall in the middle, goes negative on
+ // each side, then oscillates 2 more times. It gives nice sharp edges.
+ RESIZE_LANCZOS3,
+
+ // Lanczos filter + subpixel interpolation. If subpixel rendering is not
+ // appropriate we automatically fall back to Lanczos.
+ RESIZE_SUBPIXEL,
+
+ // enum aliases for first and last methods by algorithm or by quality.
+ RESIZE_FIRST_QUALITY_METHOD = RESIZE_GOOD,
+ RESIZE_LAST_QUALITY_METHOD = RESIZE_BEST,
+ RESIZE_FIRST_ALGORITHM_METHOD = RESIZE_BOX,
+ RESIZE_LAST_ALGORITHM_METHOD = RESIZE_SUBPIXEL,
+ };
+
+ // Resizes the given source bitmap using the specified resize method, so that
+ // the entire image is (dest_size) big. The dest_subset is the rectangle in
+ // this destination image that should actually be returned.
+ //
+ // The output image will be (dest_subset.width(), dest_subset.height()). This
+ // will save work if you do not need the entire bitmap.
+ //
+ // The destination subset must be smaller than the destination image.
+ static SkBitmap Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ SkBitmap::Allocator* allocator = NULL);
+
+ // Alternate version for resizing and returning the entire bitmap rather than
+ // a subset.
+ static SkBitmap Resize(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ SkBitmap::Allocator* allocator = NULL);
+
+ private:
+ ImageOperations(); // Class for scoping only.
+
+ // Supports all methods except RESIZE_SUBPIXEL.
+ static SkBitmap ResizeBasic(const SkBitmap& source,
+ ResizeMethod method,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ SkBitmap::Allocator* allocator = NULL);
+
+ // Subpixel renderer.
+ static SkBitmap ResizeSubpixel(const SkBitmap& source,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset,
+ SkBitmap::Allocator* allocator = NULL);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_IMAGE_OPERATIONS_H_
diff --git a/chromium/skia/ext/image_operations_bench.cc b/chromium/skia/ext/image_operations_bench.cc
new file mode 100644
index 00000000000..11263f3d788
--- /dev/null
+++ b/chromium/skia/ext/image_operations_bench.cc
@@ -0,0 +1,293 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This small program is used to measure the performance of the various
+// resize algorithms offered by the ImageOperations::Resize function.
+// It will generate an empty source bitmap, and rescale it to specified
+// dimensions. It will repeat this operation multiple time to get more accurate
+// average throughput. Because it uses elapsed time to do its math, it is only
+// accurate on an idle system (but that approach was deemed more accurate
+// than the use of the times() call.
+// To present a single number in MB/s, it calculates the 'speed' by taking
+// source surface + destination surface and dividing by the elapsed time.
+// This number is somewhat reasonable way to measure this, given our current
+// implementation which somewhat scales this way.
+
+#include <stdio.h>
+
+#include "base/basictypes.h"
+#include "base/command_line.h"
+#include "base/format_macros.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_split.h"
+#include "base/strings/string_util.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/time/time.h"
+#include "skia/ext/image_operations.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkRect.h"
+
+namespace {
+
+struct StringMethodPair {
+ const char* name;
+ skia::ImageOperations::ResizeMethod method;
+};
+#define ADD_METHOD(x) { #x, skia::ImageOperations::RESIZE_##x }
+const StringMethodPair resize_methods[] = {
+ ADD_METHOD(GOOD),
+ ADD_METHOD(BETTER),
+ ADD_METHOD(BEST),
+ ADD_METHOD(BOX),
+ ADD_METHOD(HAMMING1),
+ ADD_METHOD(LANCZOS2),
+ ADD_METHOD(LANCZOS3),
+ ADD_METHOD(SUBPIXEL)
+};
+
+// converts a string into one of the image operation method to resize.
+// Returns true on success, false otherwise.
+bool StringToMethod(const std::string& arg,
+ skia::ImageOperations::ResizeMethod* method) {
+ for (size_t i = 0; i < arraysize(resize_methods); ++i) {
+ if (base::strcasecmp(arg.c_str(), resize_methods[i].name) == 0) {
+ *method = resize_methods[i].method;
+ return true;
+ }
+ }
+ return false;
+}
+
+const char* MethodToString(skia::ImageOperations::ResizeMethod method) {
+ for (size_t i = 0; i < arraysize(resize_methods); ++i) {
+ if (method == resize_methods[i].method) {
+ return resize_methods[i].name;
+ }
+ }
+ return "unknown";
+}
+
+// Prints all supported resize methods
+void PrintMethods() {
+ bool print_comma = false;
+ for (size_t i = 0; i < arraysize(resize_methods); ++i) {
+ if (print_comma) {
+ printf(",");
+ } else {
+ print_comma = true;
+ }
+ printf(" %s", resize_methods[i].name);
+ }
+}
+
+// Returns the number of bytes that the bitmap has. This number is different
+// from what SkBitmap::getSize() returns since it does not take into account
+// the stride. The difference between the stride and the width can be large
+// because of the alignment constraints on bitmaps created for SRB scaling
+// (32 pixels) as seen on GTV platforms. Using this metric instead of the
+// getSize seemed to be a more accurate representation of the work done (even
+// though in terms of memory bandwidth that might be similar because of the
+// cache line size).
+int GetBitmapSize(const SkBitmap* bitmap) {
+ return bitmap->height() * bitmap->bytesPerPixel() * bitmap->width();
+}
+
+// Simple class to represent dimensions of a bitmap (width, height).
+class Dimensions {
+ public:
+ Dimensions()
+ : width_(0),
+ height_(0) {}
+
+ void set(int w, int h) {
+ width_ = w;
+ height_ = h;
+ }
+
+ int width() const {
+ return width_;
+ }
+
+ int height() const {
+ return height_;
+ }
+
+ bool IsValid() const {
+ return (width_ > 0 && height_ > 0);
+ }
+
+ // On failure, will set its state in such a way that IsValid will return
+ // false.
+ void FromString(const std::string& arg) {
+ std::vector<std::string> strings;
+ base::SplitString(std::string(arg), 'x', &strings);
+ if (strings.size() != 2 ||
+ base::StringToInt(strings[0], &width_) == false ||
+ base::StringToInt(strings[1], &height_) == false) {
+ width_ = -1; // force the dimension object to be invalid.
+ }
+ }
+ private:
+ int width_;
+ int height_;
+};
+
+// main class used for the benchmarking.
+class Benchmark {
+ public:
+ static const int kDefaultNumberIterations;
+ static const skia::ImageOperations::ResizeMethod kDefaultResizeMethod;
+
+ Benchmark()
+ : num_iterations_(kDefaultNumberIterations),
+ method_(kDefaultResizeMethod) {}
+
+ // Returns true if command line parsing was successful, false otherwise.
+ bool ParseArgs(const CommandLine* command_line);
+
+ // Returns true if successful, false otherwise.
+ bool Run() const;
+
+ static void Usage();
+ private:
+ int num_iterations_;
+ skia::ImageOperations::ResizeMethod method_;
+ Dimensions source_;
+ Dimensions dest_;
+};
+
+// static
+const int Benchmark::kDefaultNumberIterations = 1024;
+const skia::ImageOperations::ResizeMethod Benchmark::kDefaultResizeMethod =
+ skia::ImageOperations::RESIZE_LANCZOS3;
+
+// argument management
+void Benchmark::Usage() {
+ printf("image_operations_bench -source wxh -destination wxh "
+ "[-iterations i] [-method m] [-help]\n"
+ " -source wxh: specify source width and height\n"
+ " -destination wxh: specify destination width and height\n"
+ " -iter i: perform i iterations (default:%d)\n"
+ " -method m: use method m (default:%s), which can be:",
+ Benchmark::kDefaultNumberIterations,
+ MethodToString(Benchmark::kDefaultResizeMethod));
+ PrintMethods();
+ printf("\n -help: prints this help and exits\n");
+}
+
+bool Benchmark::ParseArgs(const CommandLine* command_line) {
+ const CommandLine::SwitchMap& switches = command_line->GetSwitches();
+ bool fNeedHelp = false;
+
+ for (CommandLine::SwitchMap::const_iterator iter = switches.begin();
+ iter != switches.end();
+ ++iter) {
+ const std::string& s = iter->first;
+ std::string value;
+#if defined(OS_WIN)
+ value = WideToUTF8(iter->second);
+#else
+ value = iter->second;
+#endif
+ if (s == "source") {
+ source_.FromString(value);
+ } else if (s == "destination") {
+ dest_.FromString(value);
+ } else if (s == "iterations") {
+ if (base::StringToInt(value, &num_iterations_) == false) {
+ fNeedHelp = true;
+ }
+ } else if (s == "method") {
+ if (!StringToMethod(value, &method_)) {
+ printf("Invalid method '%s' specified\n", value.c_str());
+ fNeedHelp = true;
+ }
+ } else {
+ fNeedHelp = true;
+ }
+ }
+
+ if (num_iterations_ <= 0) {
+ printf("Invalid number of iterations: %d\n", num_iterations_);
+ fNeedHelp = true;
+ }
+ if (!source_.IsValid()) {
+ printf("Invalid source dimensions specified\n");
+ fNeedHelp = true;
+ }
+ if (!dest_.IsValid()) {
+ printf("Invalid dest dimensions specified\n");
+ fNeedHelp = true;
+ }
+ if (fNeedHelp == true) {
+ return false;
+ }
+ return true;
+}
+
+// actual benchmark.
+bool Benchmark::Run() const {
+ SkBitmap source;
+ source.setConfig(SkBitmap::kARGB_8888_Config,
+ source_.width(), source_.height());
+ source.allocPixels();
+ source.eraseARGB(0, 0, 0, 0);
+
+ SkBitmap dest;
+
+ const base::TimeTicks start = base::TimeTicks::Now();
+
+ for (int i = 0; i < num_iterations_; ++i) {
+ dest = skia::ImageOperations::Resize(source,
+ method_,
+ dest_.width(), dest_.height());
+ }
+
+ const int64 elapsed_us = (base::TimeTicks::Now() - start).InMicroseconds();
+
+ const uint64 num_bytes = static_cast<uint64>(num_iterations_) *
+ (GetBitmapSize(&source) + GetBitmapSize(&dest));
+
+ printf("%" PRIu64 " MB/s,\telapsed = %" PRIu64 " source=%d dest=%d\n",
+ static_cast<uint64>(elapsed_us == 0 ? 0 : num_bytes / elapsed_us),
+ static_cast<uint64>(elapsed_us),
+ GetBitmapSize(&source), GetBitmapSize(&dest));
+
+ return true;
+}
+
+// A small class to automatically call Reset on the global command line to
+// avoid nasty valgrind complaints for the leak of the global command line.
+class CommandLineAutoReset {
+ public:
+ CommandLineAutoReset(int argc, char** argv) {
+ CommandLine::Init(argc, argv);
+ }
+ ~CommandLineAutoReset() {
+ CommandLine::Reset();
+ }
+
+ const CommandLine* Get() const {
+ return CommandLine::ForCurrentProcess();
+ }
+};
+
+} // namespace
+
+int main(int argc, char** argv) {
+ Benchmark bench;
+ CommandLineAutoReset command_line(argc, argv);
+
+ if (!bench.ParseArgs(command_line.Get())) {
+ Benchmark::Usage();
+ return 1;
+ }
+
+ if (!bench.Run()) {
+ printf("Failed to run benchmark\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/chromium/skia/ext/image_operations_unittest.cc b/chromium/skia/ext/image_operations_unittest.cc
new file mode 100644
index 00000000000..c7069e28172
--- /dev/null
+++ b/chromium/skia/ext/image_operations_unittest.cc
@@ -0,0 +1,731 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <cmath>
+#include <iomanip>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
+#include "base/strings/string_util.h"
+#include "skia/ext/image_operations.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/size.h"
+
+namespace {
+
+// Computes the average pixel value for the given range, inclusive.
+uint32_t AveragePixel(const SkBitmap& bmp,
+ int x_min, int x_max,
+ int y_min, int y_max) {
+ float accum[4] = {0, 0, 0, 0};
+ int count = 0;
+ for (int y = y_min; y <= y_max; y++) {
+ for (int x = x_min; x <= x_max; x++) {
+ uint32_t cur = *bmp.getAddr32(x, y);
+ accum[0] += SkColorGetB(cur);
+ accum[1] += SkColorGetG(cur);
+ accum[2] += SkColorGetR(cur);
+ accum[3] += SkColorGetA(cur);
+ count++;
+ }
+ }
+
+ return SkColorSetARGB(static_cast<unsigned char>(accum[3] / count),
+ static_cast<unsigned char>(accum[2] / count),
+ static_cast<unsigned char>(accum[1] / count),
+ static_cast<unsigned char>(accum[0] / count));
+}
+
+// Computes the average pixel (/color) value for the given colors.
+SkColor AveragePixel(const SkColor colors[], size_t color_count) {
+ float accum[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+ for (size_t i = 0; i < color_count; ++i) {
+ const SkColor cur = colors[i];
+ accum[0] += static_cast<float>(SkColorGetA(cur));
+ accum[1] += static_cast<float>(SkColorGetR(cur));
+ accum[2] += static_cast<float>(SkColorGetG(cur));
+ accum[3] += static_cast<float>(SkColorGetB(cur));
+ }
+ const SkColor average_color =
+ SkColorSetARGB(static_cast<uint8_t>(accum[0] / color_count),
+ static_cast<uint8_t>(accum[1] / color_count),
+ static_cast<uint8_t>(accum[2] / color_count),
+ static_cast<uint8_t>(accum[3] / color_count));
+ return average_color;
+}
+
+void PrintPixel(const SkBitmap& bmp,
+ int x_min, int x_max,
+ int y_min, int y_max) {
+ char str[128];
+
+ for (int y = y_min; y <= y_max; ++y) {
+ for (int x = x_min; x <= x_max; ++x) {
+ const uint32_t cur = *bmp.getAddr32(x, y);
+ base::snprintf(str, sizeof(str), "bmp[%d,%d] = %08X", x, y, cur);
+ ADD_FAILURE() << str;
+ }
+ }
+}
+
+// Returns the euclidian distance between two RGBA colors interpreted
+// as 4-components vectors.
+//
+// Notes:
+// - This is a really poor definition of color distance. Yet it
+// is "good enough" for our uses here.
+// - More realistic measures like the various Delta E formulas defined
+// by CIE are way more complex and themselves require the RGBA to
+// to transformed into CIELAB (typically via sRGB first).
+// - The static_cast<int> below are needed to avoid interpreting "negative"
+// differences as huge positive values.
+float ColorsEuclidianDistance(const SkColor a, const SkColor b) {
+ int b_int_diff = static_cast<int>(SkColorGetB(a) - SkColorGetB(b));
+ int g_int_diff = static_cast<int>(SkColorGetG(a) - SkColorGetG(b));
+ int r_int_diff = static_cast<int>(SkColorGetR(a) - SkColorGetR(b));
+ int a_int_diff = static_cast<int>(SkColorGetA(a) - SkColorGetA(b));
+
+ float b_float_diff = static_cast<float>(b_int_diff);
+ float g_float_diff = static_cast<float>(g_int_diff);
+ float r_float_diff = static_cast<float>(r_int_diff);
+ float a_float_diff = static_cast<float>(a_int_diff);
+
+ return sqrtf((b_float_diff * b_float_diff) + (g_float_diff * g_float_diff) +
+ (r_float_diff * r_float_diff) + (a_float_diff * a_float_diff));
+}
+
+// Returns true if each channel of the given two colors are "close." This is
+// used for comparing colors where rounding errors may cause off-by-one.
+bool ColorsClose(uint32_t a, uint32_t b) {
+ return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2 &&
+ abs(static_cast<int>(SkColorGetA(a) - SkColorGetA(b))) < 2;
+}
+
+void FillDataToBitmap(int w, int h, SkBitmap* bmp) {
+ bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bmp->allocPixels();
+
+ for (int y = 0; y < h; ++y) {
+ for (int x = 0; x < w; ++x) {
+ const uint8_t component = static_cast<uint8_t>(y * w + x);
+ const SkColor pixel = SkColorSetARGB(component, component,
+ component, component);
+ *bmp->getAddr32(x, y) = pixel;
+ }
+ }
+}
+
+// Draws a horizontal and vertical grid into the w x h bitmap passed in.
+// Each line in the grid is drawn with a width of "grid_width" pixels,
+// and those lines repeat every "grid_pitch" pixels. The top left pixel (0, 0)
+// is considered to be part of a grid line.
+// The pixels that fall on a line are colored with "grid_color", while those
+// outside of the lines are colored in "background_color".
+// Note that grid_with can be greather than or equal to grid_pitch, in which
+// case the resulting bitmap will be a solid color "grid_color".
+void DrawGridToBitmap(int w, int h,
+ SkColor background_color, SkColor grid_color,
+ int grid_pitch, int grid_width,
+ SkBitmap* bmp) {
+ ASSERT_GT(grid_pitch, 0);
+ ASSERT_GT(grid_width, 0);
+ ASSERT_NE(background_color, grid_color);
+
+ bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bmp->allocPixels();
+
+ for (int y = 0; y < h; ++y) {
+ bool y_on_grid = ((y % grid_pitch) < grid_width);
+
+ for (int x = 0; x < w; ++x) {
+ bool on_grid = (y_on_grid || ((x % grid_pitch) < grid_width));
+
+ *bmp->getAddr32(x, y) = (on_grid ? grid_color : background_color);
+ }
+ }
+}
+
+// Draws a checkerboard pattern into the w x h bitmap passed in.
+// Each rectangle is rect_w in width, rect_h in height.
+// The colors alternate between color1 and color2, color1 being used
+// in the rectangle at the top left corner.
+void DrawCheckerToBitmap(int w, int h,
+ SkColor color1, SkColor color2,
+ int rect_w, int rect_h,
+ SkBitmap* bmp) {
+ ASSERT_GT(rect_w, 0);
+ ASSERT_GT(rect_h, 0);
+ ASSERT_NE(color1, color2);
+
+ bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+ bmp->allocPixels();
+
+ for (int y = 0; y < h; ++y) {
+ bool y_bit = (((y / rect_h) & 0x1) == 0);
+
+ for (int x = 0; x < w; ++x) {
+ bool x_bit = (((x / rect_w) & 0x1) == 0);
+
+ bool use_color2 = (x_bit != y_bit); // xor
+
+ *bmp->getAddr32(x, y) = (use_color2 ? color2 : color1);
+ }
+ }
+}
+
+// DEBUG_BITMAP_GENERATION (0 or 1) controls whether the routines
+// to save the test bitmaps are present. By default the test just fails
+// without reading/writing files but it is then convenient to have
+// a simple way to make the failing tests write out the input/output images
+// to check them visually.
+#define DEBUG_BITMAP_GENERATION (0)
+
+#if DEBUG_BITMAP_GENERATION
+void SaveBitmapToPNG(const SkBitmap& bmp, const char* path) {
+ SkAutoLockPixels lock(bmp);
+ std::vector<unsigned char> png;
+ gfx::PNGCodec::ColorFormat color_format = gfx::PNGCodec::FORMAT_RGBA;
+ if (!gfx::PNGCodec::Encode(
+ reinterpret_cast<const unsigned char*>(bmp.getPixels()),
+ color_format, gfx::Size(bmp.width(), bmp.height()),
+ static_cast<int>(bmp.rowBytes()),
+ false, std::vector<gfx::PNGCodec::Comment>(), &png)) {
+ FAIL() << "Failed to encode image";
+ }
+
+ const base::FilePath fpath(path);
+ const int num_written =
+ file_util::WriteFile(fpath, reinterpret_cast<const char*>(&png[0]),
+ png.size());
+ if (num_written != static_cast<int>(png.size())) {
+ FAIL() << "Failed to write dest \"" << path << '"';
+ }
+}
+#endif // #if DEBUG_BITMAP_GENERATION
+
+void CheckResampleToSame(skia::ImageOperations::ResizeMethod method) {
+ // Make our source bitmap.
+ const int src_w = 16, src_h = 34;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ // Do a resize of the full bitmap to the same size. The lanczos filter is good
+ // enough that we should get exactly the same image for output.
+ SkBitmap results = skia::ImageOperations::Resize(src, method, src_w, src_h);
+ ASSERT_EQ(src_w, results.width());
+ ASSERT_EQ(src_h, results.height());
+
+ SkAutoLockPixels src_lock(src);
+ SkAutoLockPixels results_lock(results);
+ for (int y = 0; y < src_h; y++) {
+ for (int x = 0; x < src_w; x++) {
+ EXPECT_EQ(*src.getAddr32(x, y), *results.getAddr32(x, y));
+ }
+ }
+}
+
+// Types defined outside of the ResizeShouldAverageColors test to allow
+// use of the arraysize() macro.
+//
+// 'max_color_distance_override' is used in a max() call together with
+// the value of 'max_color_distance' defined in a TestedPixel instance.
+// Hence a value of 0.0 in 'max_color_distance_override' means
+// "use the pixel-specific value" and larger values can be used to allow
+// worse computation errors than provided in a TestedPixel instance.
+struct TestedResizeMethod {
+ skia::ImageOperations::ResizeMethod method;
+ const char* name;
+ float max_color_distance_override;
+};
+
+struct TestedPixel {
+ int x;
+ int y;
+ float max_color_distance;
+ const char* name;
+};
+
+// Helper function used by the test "ResizeShouldAverageColors" below.
+// Note that ASSERT_EQ does a "return;" on failure, hence we can't have
+// a "bool" return value to reflect success. Hence "all_pixels_pass"
+void CheckResizeMethodShouldAverageGrid(
+ const SkBitmap& src,
+ const TestedResizeMethod& tested_method,
+ int dest_w, int dest_h, SkColor average_color,
+ bool* method_passed) {
+ *method_passed = false;
+
+ const TestedPixel tested_pixels[] = {
+ // Corners
+ { 0, 0, 2.3f, "Top left corner" },
+ { 0, dest_h - 1, 2.3f, "Bottom left corner" },
+ { dest_w - 1, 0, 2.3f, "Top right corner" },
+ { dest_w - 1, dest_h - 1, 2.3f, "Bottom right corner" },
+ // Middle points of each side
+ { dest_w / 2, 0, 1.0f, "Top middle" },
+ { dest_w / 2, dest_h - 1, 1.0f, "Bottom middle" },
+ { 0, dest_h / 2, 1.0f, "Left middle" },
+ { dest_w - 1, dest_h / 2, 1.0f, "Right middle" },
+ // Center
+ { dest_w / 2, dest_h / 2, 1.0f, "Center" }
+ };
+
+ // Resize the src
+ const skia::ImageOperations::ResizeMethod method = tested_method.method;
+
+ SkBitmap dest = skia::ImageOperations::Resize(src, method, dest_w, dest_h);
+ ASSERT_EQ(dest_w, dest.width());
+ ASSERT_EQ(dest_h, dest.height());
+
+ // Check that pixels match the expected average.
+ float max_observed_distance = 0.0f;
+ bool all_pixels_ok = true;
+
+ SkAutoLockPixels dest_lock(dest);
+
+ for (size_t pixel_index = 0;
+ pixel_index < arraysize(tested_pixels);
+ ++pixel_index) {
+ const TestedPixel& tested_pixel = tested_pixels[pixel_index];
+
+ const int x = tested_pixel.x;
+ const int y = tested_pixel.y;
+ const float max_allowed_distance =
+ std::max(tested_pixel.max_color_distance,
+ tested_method.max_color_distance_override);
+
+ const SkColor actual_color = *dest.getAddr32(x, y);
+
+ // Check that the pixels away from the border region are very close
+ // to the expected average color
+ float distance = ColorsEuclidianDistance(average_color, actual_color);
+
+ EXPECT_LE(distance, max_allowed_distance)
+ << "Resizing method: " << tested_method.name
+ << ", pixel tested: " << tested_pixel.name
+ << "(" << x << ", " << y << ")"
+ << std::hex << std::showbase
+ << ", expected (avg) hex: " << average_color
+ << ", actual hex: " << actual_color;
+
+ if (distance > max_allowed_distance) {
+ all_pixels_ok = false;
+ }
+ if (distance > max_observed_distance) {
+ max_observed_distance = distance;
+ }
+ }
+
+ if (!all_pixels_ok) {
+ ADD_FAILURE() << "Maximum observed color distance for method "
+ << tested_method.name << ": " << max_observed_distance;
+
+#if DEBUG_BITMAP_GENERATION
+ char path[128];
+ base::snprintf(path, sizeof(path),
+ "/tmp/ResizeShouldAverageColors_%s_dest.png",
+ tested_method.name);
+ SaveBitmapToPNG(dest, path);
+#endif // #if DEBUG_BITMAP_GENERATION
+ }
+
+ *method_passed = all_pixels_ok;
+}
+
+
+} // namespace
+
+// Helper tests that saves bitmaps to PNGs in /tmp/ to visually check
+// that the bitmap generation functions work as expected.
+// Those tests are not enabled by default as verification is done
+// manually/visually, however it is convenient to leave the functions
+// in place.
+#if 0 && DEBUG_BITMAP_GENERATION
+TEST(ImageOperations, GenerateGradientBitmap) {
+ // Make our source bitmap.
+ const int src_w = 640, src_h = 480;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ SaveBitmapToPNG(src, "/tmp/gradient_640x480.png");
+}
+
+TEST(ImageOperations, GenerateGridBitmap) {
+ const int src_w = 640, src_h = 480, src_grid_pitch = 10, src_grid_width = 4;
+ const SkColor grid_color = SK_ColorRED, background_color = SK_ColorBLUE;
+ SkBitmap src;
+ DrawGridToBitmap(src_w, src_h,
+ background_color, grid_color,
+ src_grid_pitch, src_grid_width,
+ &src);
+
+ SaveBitmapToPNG(src, "/tmp/grid_640x408_10_4_red_blue.png");
+}
+
+TEST(ImageOperations, GenerateCheckerBitmap) {
+ const int src_w = 640, src_h = 480, rect_w = 10, rect_h = 4;
+ const SkColor color1 = SK_ColorRED, color2 = SK_ColorBLUE;
+ SkBitmap src;
+ DrawCheckerToBitmap(src_w, src_h, color1, color2, rect_w, rect_h, &src);
+
+ SaveBitmapToPNG(src, "/tmp/checker_640x408_10_4_red_blue.png");
+}
+#endif // #if ... && DEBUG_BITMAP_GENERATION
+
+// Makes the bitmap 50% the size as the original using a box filter. This is
+// an easy operation that we can check the results for manually.
+TEST(ImageOperations, Halve) {
+ // Make our source bitmap.
+ int src_w = 30, src_h = 38;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ // Do a halving of the full bitmap.
+ SkBitmap actual_results = skia::ImageOperations::Resize(
+ src, skia::ImageOperations::RESIZE_BOX, src_w / 2, src_h / 2);
+ ASSERT_EQ(src_w / 2, actual_results.width());
+ ASSERT_EQ(src_h / 2, actual_results.height());
+
+ // Compute the expected values & compare.
+ SkAutoLockPixels lock(actual_results);
+ for (int y = 0; y < actual_results.height(); y++) {
+ for (int x = 0; x < actual_results.width(); x++) {
+ // Note that those expressions take into account the "half-pixel"
+ // offset that comes into play due to considering the coordinates
+ // of the center of the pixels. So x * 2 is a simplification
+ // of ((x+0.5) * 2 - 1) and (x * 2 + 1) is really (x + 0.5) * 2.
+ int first_x = x * 2;
+ int last_x = std::min(src_w - 1, x * 2 + 1);
+
+ int first_y = y * 2;
+ int last_y = std::min(src_h - 1, y * 2 + 1);
+
+ const uint32_t expected_color = AveragePixel(src,
+ first_x, last_x,
+ first_y, last_y);
+ const uint32_t actual_color = *actual_results.getAddr32(x, y);
+ const bool close = ColorsClose(expected_color, actual_color);
+ EXPECT_TRUE(close);
+ if (!close) {
+ char str[128];
+ base::snprintf(str, sizeof(str),
+ "exp[%d,%d] = %08X, actual[%d,%d] = %08X",
+ x, y, expected_color, x, y, actual_color);
+ ADD_FAILURE() << str;
+ PrintPixel(src, first_x, last_x, first_y, last_y);
+ }
+ }
+ }
+}
+
+TEST(ImageOperations, HalveSubset) {
+ // Make our source bitmap.
+ int src_w = 16, src_h = 34;
+ SkBitmap src;
+ FillDataToBitmap(src_w, src_h, &src);
+
+ // Do a halving of the full bitmap.
+ SkBitmap full_results = skia::ImageOperations::Resize(
+ src, skia::ImageOperations::RESIZE_BOX, src_w / 2, src_h / 2);
+ ASSERT_EQ(src_w / 2, full_results.width());
+ ASSERT_EQ(src_h / 2, full_results.height());
+
+ // Now do a halving of a a subset, recall the destination subset is in the
+ // destination coordinate system (max = half of the original image size).
+ SkIRect subset_rect = { 2, 3, 3, 6 };
+ SkBitmap subset_results = skia::ImageOperations::Resize(
+ src, skia::ImageOperations::RESIZE_BOX,
+ src_w / 2, src_h / 2, subset_rect);
+ ASSERT_EQ(subset_rect.width(), subset_results.width());
+ ASSERT_EQ(subset_rect.height(), subset_results.height());
+
+ // The computed subset and the corresponding subset of the original image
+ // should be the same.
+ SkAutoLockPixels full_lock(full_results);
+ SkAutoLockPixels subset_lock(subset_results);
+ for (int y = 0; y < subset_rect.height(); y++) {
+ for (int x = 0; x < subset_rect.width(); x++) {
+ ASSERT_EQ(
+ *full_results.getAddr32(x + subset_rect.fLeft, y + subset_rect.fTop),
+ *subset_results.getAddr32(x, y));
+ }
+ }
+}
+
+TEST(ImageOperations, InvalidParams) {
+ // Make our source bitmap.
+ SkBitmap src;
+ src.setConfig(SkBitmap::kA8_Config, 16, 34);
+ src.allocPixels();
+
+ // Scale it, don't die.
+ SkBitmap full_results = skia::ImageOperations::Resize(
+ src, skia::ImageOperations::RESIZE_BOX, 10, 20);
+}
+
+// Resamples an image to the same image, it should give the same result.
+TEST(ImageOperations, ResampleToSameHamming1) {
+ CheckResampleToSame(skia::ImageOperations::RESIZE_HAMMING1);
+}
+
+TEST(ImageOperations, ResampleToSameLanczos2) {
+ CheckResampleToSame(skia::ImageOperations::RESIZE_LANCZOS2);
+}
+
+TEST(ImageOperations, ResampleToSameLanczos3) {
+ CheckResampleToSame(skia::ImageOperations::RESIZE_LANCZOS3);
+}
+
+// Check that all Good/Better/Best, Box, Lanczos2 and Lanczos3 generate purple
+// when resizing a 4x8 red/blue checker pattern by 1/16x1/16.
+TEST(ImageOperations, ResizeShouldAverageColors) {
+ // Make our source bitmap.
+ const int src_w = 640, src_h = 480, checker_rect_w = 4, checker_rect_h = 8;
+ const SkColor checker_color1 = SK_ColorRED, checker_color2 = SK_ColorBLUE;
+
+ const int dest_w = src_w / (4 * checker_rect_w);
+ const int dest_h = src_h / (2 * checker_rect_h);
+
+ // Compute the expected (average) color
+ const SkColor colors[] = { checker_color1, checker_color2 };
+ const SkColor average_color = AveragePixel(colors, arraysize(colors));
+
+ // RESIZE_SUBPIXEL is only supported on Linux/non-GTV platforms.
+ static const TestedResizeMethod tested_methods[] = {
+ { skia::ImageOperations::RESIZE_GOOD, "GOOD", 0.0f },
+ { skia::ImageOperations::RESIZE_BETTER, "BETTER", 0.0f },
+ { skia::ImageOperations::RESIZE_BEST, "BEST", 0.0f },
+ { skia::ImageOperations::RESIZE_BOX, "BOX", 0.0f },
+ { skia::ImageOperations::RESIZE_HAMMING1, "HAMMING1", 0.0f },
+ { skia::ImageOperations::RESIZE_LANCZOS2, "LANCZOS2", 0.0f },
+ { skia::ImageOperations::RESIZE_LANCZOS3, "LANCZOS3", 0.0f },
+#if defined(OS_LINUX) && !defined(GTV)
+ // SUBPIXEL has slightly worse performance than the other filters:
+ // 6.324 Bottom left/right corners
+ // 5.099 Top left/right corners
+ // 2.828 Bottom middle
+ // 1.414 Top/Left/Right middle, center
+ //
+ // This is expected since, in order to judge RESIZE_SUBPIXEL accurately,
+ // we'd need to compute distances for each sub-pixel, and potentially
+ // tweak the test parameters so that expectations were realistic when
+ // looking at sub-pixels in isolation.
+ //
+ // Rather than going to these lengths, we added the "max_distance_override"
+ // field in TestedResizeMethod, intended for RESIZE_SUBPIXEL. It allows
+ // us to to enable its testing without having to lower the success criteria
+ // for the other methods. This procedure is distateful but defining
+ // a distance limit for each tested pixel for each method was judged to add
+ // unneeded complexity.
+ { skia::ImageOperations::RESIZE_SUBPIXEL, "SUBPIXEL", 6.4f },
+#endif
+ };
+
+ // Create our source bitmap.
+ SkBitmap src;
+ DrawCheckerToBitmap(src_w, src_h,
+ checker_color1, checker_color2,
+ checker_rect_w, checker_rect_h,
+ &src);
+
+ // For each method, downscale by 16 in each dimension,
+ // and check each tested pixel against the expected average color.
+ bool all_methods_ok ALLOW_UNUSED = true;
+
+ for (size_t method_index = 0;
+ method_index < arraysize(tested_methods);
+ ++method_index) {
+ bool pass = true;
+ CheckResizeMethodShouldAverageGrid(src,
+ tested_methods[method_index],
+ dest_w, dest_h, average_color,
+ &pass);
+ if (!pass) {
+ all_methods_ok = false;
+ }
+ }
+
+#if DEBUG_BITMAP_GENERATION
+ if (!all_methods_ok) {
+ SaveBitmapToPNG(src, "/tmp/ResizeShouldAverageColors_src.png");
+ }
+#endif // #if DEBUG_BITMAP_GENERATION
+}
+
+
+// Check that Lanczos2 and Lanczos3 thumbnails produce similar results
+TEST(ImageOperations, CompareLanczosMethods) {
+ const int src_w = 640, src_h = 480, src_grid_pitch = 8, src_grid_width = 4;
+
+ const int dest_w = src_w / 4;
+ const int dest_h = src_h / 4;
+
+ // 5.0f is the maximum distance we see in this test given the current
+ // parameters. The value is very ad-hoc and the parameters of the scaling
+ // were picked to produce a small value. So this test is very much about
+ // revealing egregious regression rather than doing a good job at checking
+ // the math behind the filters.
+ // TODO(evannier): because of the half pixel error mentioned inside
+ // image_operations.cc, this distance is much larger than it should be.
+ // This should read:
+ // const float max_color_distance = 5.0f;
+ const float max_color_distance = 12.1f;
+
+ // Make our source bitmap.
+ SkColor grid_color = SK_ColorRED, background_color = SK_ColorBLUE;
+ SkBitmap src;
+ DrawGridToBitmap(src_w, src_h,
+ background_color, grid_color,
+ src_grid_pitch, src_grid_width,
+ &src);
+
+ // Resize the src using both methods.
+ SkBitmap dest_l2 = skia::ImageOperations::Resize(
+ src,
+ skia::ImageOperations::RESIZE_LANCZOS2,
+ dest_w, dest_h);
+ ASSERT_EQ(dest_w, dest_l2.width());
+ ASSERT_EQ(dest_h, dest_l2.height());
+
+ SkBitmap dest_l3 = skia::ImageOperations::Resize(
+ src,
+ skia::ImageOperations::RESIZE_LANCZOS3,
+ dest_w, dest_h);
+ ASSERT_EQ(dest_w, dest_l3.width());
+ ASSERT_EQ(dest_h, dest_l3.height());
+
+ // Compare the pixels produced by both methods.
+ float max_observed_distance = 0.0f;
+ bool all_pixels_ok = true;
+
+ SkAutoLockPixels l2_lock(dest_l2);
+ SkAutoLockPixels l3_lock(dest_l3);
+ for (int y = 0; y < dest_h; ++y) {
+ for (int x = 0; x < dest_w; ++x) {
+ const SkColor color_lanczos2 = *dest_l2.getAddr32(x, y);
+ const SkColor color_lanczos3 = *dest_l3.getAddr32(x, y);
+
+ float distance = ColorsEuclidianDistance(color_lanczos2, color_lanczos3);
+
+ EXPECT_LE(distance, max_color_distance)
+ << "pixel tested: (" << x << ", " << y
+ << std::hex << std::showbase
+ << "), lanczos2 hex: " << color_lanczos2
+ << ", lanczos3 hex: " << color_lanczos3
+ << std::setprecision(2)
+ << ", distance: " << distance;
+
+ if (distance > max_color_distance) {
+ all_pixels_ok = false;
+ }
+ if (distance > max_observed_distance) {
+ max_observed_distance = distance;
+ }
+ }
+ }
+
+ if (!all_pixels_ok) {
+ ADD_FAILURE() << "Maximum observed color distance: "
+ << max_observed_distance;
+
+#if DEBUG_BITMAP_GENERATION
+ SaveBitmapToPNG(src, "/tmp/CompareLanczosMethods_source.png");
+ SaveBitmapToPNG(dest_l2, "/tmp/CompareLanczosMethods_lanczos2.png");
+ SaveBitmapToPNG(dest_l3, "/tmp/CompareLanczosMethods_lanczos3.png");
+#endif // #if DEBUG_BITMAP_GENERATION
+ }
+}
+
+#ifndef M_PI
+// No M_PI in math.h on windows? No problem.
+#define M_PI 3.14159265358979323846
+#endif
+
+static double sinc(double x) {
+ if (x == 0.0) return 1.0;
+ x *= M_PI;
+ return sin(x) / x;
+}
+
+static double lanczos3(double offset) {
+ if (fabs(offset) >= 3) return 0.0;
+ return sinc(offset) * sinc(offset / 3.0);
+}
+
+TEST(ImageOperations, ScaleUp) {
+ const int src_w = 3;
+ const int src_h = 3;
+ const int dst_w = 9;
+ const int dst_h = 9;
+ SkBitmap src;
+ src.setConfig(SkBitmap::kARGB_8888_Config, src_w, src_h);
+ src.allocPixels();
+
+ for (int src_y = 0; src_y < src_h; ++src_y) {
+ for (int src_x = 0; src_x < src_w; ++src_x) {
+ *src.getAddr32(src_x, src_y) = SkColorSetARGBInline(255,
+ 10 + src_x * 100,
+ 10 + src_y * 100,
+ 0);
+ }
+ }
+
+ SkBitmap dst = skia::ImageOperations::Resize(
+ src,
+ skia::ImageOperations::RESIZE_LANCZOS3,
+ dst_w, dst_h);
+ SkAutoLockPixels dst_lock(dst);
+ for (int dst_y = 0; dst_y < dst_h; ++dst_y) {
+ for (int dst_x = 0; dst_x < dst_w; ++dst_x) {
+ float dst_x_in_src = (dst_x + 0.5) * src_w / dst_w;
+ float dst_y_in_src = (dst_y + 0.5) * src_h / dst_h;
+ float a = 0.0f;
+ float r = 0.0f;
+ float g = 0.0f;
+ float b = 0.0f;
+ float sum = 0.0f;
+ for (int src_y = 0; src_y < src_h; ++src_y) {
+ for (int src_x = 0; src_x < src_w; ++src_x) {
+ double coeff =
+ lanczos3(src_x + 0.5 - dst_x_in_src) *
+ lanczos3(src_y + 0.5 - dst_y_in_src);
+ sum += coeff;
+ SkColor tmp = *src.getAddr32(src_x, src_y);
+ a += coeff * SkColorGetA(tmp);
+ r += coeff * SkColorGetR(tmp);
+ g += coeff * SkColorGetG(tmp);
+ b += coeff * SkColorGetB(tmp);
+ }
+ }
+ a /= sum;
+ r /= sum;
+ g /= sum;
+ b /= sum;
+ if (a < 0.0f) a = 0.0f;
+ if (r < 0.0f) r = 0.0f;
+ if (g < 0.0f) g = 0.0f;
+ if (b < 0.0f) b = 0.0f;
+ if (a > 255.0f) a = 255.0f;
+ if (r > 255.0f) r = 255.0f;
+ if (g > 255.0f) g = 255.0f;
+ if (b > 255.0f) b = 255.0f;
+ SkColor dst_color = *dst.getAddr32(dst_x, dst_y);
+ EXPECT_LE(fabs(SkColorGetA(dst_color) - a), 1.5f);
+ EXPECT_LE(fabs(SkColorGetR(dst_color) - r), 1.5f);
+ EXPECT_LE(fabs(SkColorGetG(dst_color) - g), 1.5f);
+ EXPECT_LE(fabs(SkColorGetB(dst_color) - b), 1.5f);
+ if (HasFailure()) {
+ return;
+ }
+ }
+ }
+}
diff --git a/chromium/skia/ext/lazy_pixel_ref.cc b/chromium/skia/ext/lazy_pixel_ref.cc
new file mode 100644
index 00000000000..784fae47419
--- /dev/null
+++ b/chromium/skia/ext/lazy_pixel_ref.cc
@@ -0,0 +1,15 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/lazy_pixel_ref.h"
+
+namespace skia {
+
+LazyPixelRef::LazyPixelRef() : SkPixelRef(0) {
+}
+
+LazyPixelRef::~LazyPixelRef() {
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/lazy_pixel_ref.h b/chromium/skia/ext/lazy_pixel_ref.h
new file mode 100644
index 00000000000..fff4c385add
--- /dev/null
+++ b/chromium/skia/ext/lazy_pixel_ref.h
@@ -0,0 +1,39 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_LAZY_PIXEL_REF_H_
+#define SKIA_EXT_LAZY_PIXEL_REF_H_
+
+#include "third_party/skia/include/core/SkPixelRef.h"
+#include "third_party/skia/include/core/SkRect.h"
+
+namespace skia {
+
+// This class extends SkPixelRef to facilitate lazy image decoding on the impl
+// thread.
+class SK_API LazyPixelRef : public SkPixelRef {
+ public:
+ LazyPixelRef();
+ virtual ~LazyPixelRef();
+
+ struct PrepareParams {
+ // Clipping rect for this pixel ref.
+ SkIRect clip_rect;
+ };
+
+ // Request the ImageDecodingStore to prepare image decoding for the
+ // given clipping rect. Returns true is succeeded, or false otherwise.
+ virtual bool PrepareToDecode(const PrepareParams& params) = 0;
+
+ // Returns true if this pixel ref is already in the ImageDecodingStore's
+ // cache, false otherwise. Much cheaper than PrepareToDecode().
+ virtual bool MaybeDecoded() = 0;
+
+ // Start decoding the image.
+ virtual void Decode() = 0;
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_LAZY_PIXEL_REF_H_
diff --git a/chromium/skia/ext/lazy_pixel_ref_utils.cc b/chromium/skia/ext/lazy_pixel_ref_utils.cc
new file mode 100644
index 00000000000..67371c9035e
--- /dev/null
+++ b/chromium/skia/ext/lazy_pixel_ref_utils.cc
@@ -0,0 +1,409 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/lazy_pixel_ref_utils.h"
+
+#include "skia/ext/lazy_pixel_ref.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkDraw.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
+#include "third_party/skia/include/core/SkRRect.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "third_party/skia/src/core/SkRasterClip.h"
+
+namespace skia {
+
+namespace {
+
+// URI label for a lazily decoded SkPixelRef.
+const char kLabelLazyDecoded[] = "lazy";
+
+class LazyPixelRefSet {
+ public:
+ LazyPixelRefSet(
+ std::vector<LazyPixelRefUtils::PositionLazyPixelRef>* pixel_refs)
+ : pixel_refs_(pixel_refs) {}
+
+ void Add(SkPixelRef* pixel_ref, const SkRect& rect) {
+ // Only save lazy pixel refs.
+ if (pixel_ref->getURI() &&
+ !strcmp(pixel_ref->getURI(), kLabelLazyDecoded)) {
+ LazyPixelRefUtils::PositionLazyPixelRef position_pixel_ref;
+ position_pixel_ref.lazy_pixel_ref =
+ static_cast<skia::LazyPixelRef*>(pixel_ref);
+ position_pixel_ref.pixel_ref_rect = rect;
+ pixel_refs_->push_back(position_pixel_ref);
+ }
+ }
+
+ private:
+ std::vector<LazyPixelRefUtils::PositionLazyPixelRef>* pixel_refs_;
+};
+
+class GatherPixelRefDevice : public SkDevice {
+ public:
+ GatherPixelRefDevice(const SkBitmap& bm, LazyPixelRefSet* lazy_pixel_ref_set)
+ : SkDevice(bm), lazy_pixel_ref_set_(lazy_pixel_ref_set) {}
+
+ virtual void clear(SkColor color) SK_OVERRIDE {}
+ virtual void writePixels(const SkBitmap& bitmap,
+ int x,
+ int y,
+ SkCanvas::Config8888 config8888) SK_OVERRIDE {}
+
+ virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) SK_OVERRIDE {
+ SkBitmap bitmap;
+ if (GetBitmapFromPaint(paint, &bitmap)) {
+ SkRect clip_rect = SkRect::Make(draw.fRC->getBounds());
+ SkRect canvas_rect = SkRect::MakeWH(width(), height());
+ SkRect paint_rect = SkRect::MakeEmpty();
+ paint_rect.intersect(canvas_rect, clip_rect);
+
+ AddBitmap(bitmap, paint_rect);
+ }
+ }
+
+ virtual void drawPoints(const SkDraw& draw,
+ SkCanvas::PointMode mode,
+ size_t count,
+ const SkPoint points[],
+ const SkPaint& paint) SK_OVERRIDE {
+ SkBitmap bitmap;
+ if (!GetBitmapFromPaint(paint, &bitmap))
+ return;
+
+ if (count == 0)
+ return;
+
+ SkPoint min_point = points[0];
+ SkPoint max_point = points[0];
+ for (size_t i = 1; i < count; ++i) {
+ const SkPoint& point = points[i];
+ min_point.set(std::min(min_point.x(), point.x()),
+ std::min(min_point.y(), point.y()));
+ max_point.set(std::max(max_point.x(), point.x()),
+ std::max(max_point.y(), point.y()));
+ }
+
+ SkRect bounds = SkRect::MakeLTRB(
+ min_point.x(), min_point.y(), max_point.x(), max_point.y());
+
+ GatherPixelRefDevice::drawRect(draw, bounds, paint);
+ }
+ virtual void drawRect(const SkDraw& draw,
+ const SkRect& rect,
+ const SkPaint& paint) SK_OVERRIDE {
+ SkBitmap bitmap;
+ if (GetBitmapFromPaint(paint, &bitmap)) {
+ SkRect mapped_rect;
+ draw.fMatrix->mapRect(&mapped_rect, rect);
+ mapped_rect.intersect(SkRect::Make(draw.fRC->getBounds()));
+ AddBitmap(bitmap, mapped_rect);
+ }
+ }
+ virtual void drawOval(const SkDraw& draw,
+ const SkRect& rect,
+ const SkPaint& paint) SK_OVERRIDE {
+ GatherPixelRefDevice::drawRect(draw, rect, paint);
+ }
+ virtual void drawRRect(const SkDraw& draw,
+ const SkRRect& rect,
+ const SkPaint& paint) SK_OVERRIDE {
+ GatherPixelRefDevice::drawRect(draw, rect.rect(), paint);
+ }
+ virtual void drawPath(const SkDraw& draw,
+ const SkPath& path,
+ const SkPaint& paint,
+ const SkMatrix* pre_path_matrix,
+ bool path_is_mutable) SK_OVERRIDE {
+ SkBitmap bitmap;
+ if (!GetBitmapFromPaint(paint, &bitmap))
+ return;
+
+ SkRect path_bounds = path.getBounds();
+ SkRect final_rect;
+ if (pre_path_matrix != NULL)
+ pre_path_matrix->mapRect(&final_rect, path_bounds);
+ else
+ final_rect = path_bounds;
+
+ GatherPixelRefDevice::drawRect(draw, final_rect, paint);
+ }
+ virtual void drawBitmap(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkMatrix& matrix,
+ const SkPaint& paint) SK_OVERRIDE {
+ SkMatrix total_matrix;
+ total_matrix.setConcat(*draw.fMatrix, matrix);
+
+ SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
+ SkRect mapped_rect;
+ total_matrix.mapRect(&mapped_rect, bitmap_rect);
+ AddBitmap(bitmap, mapped_rect);
+
+ SkBitmap paint_bitmap;
+ if (GetBitmapFromPaint(paint, &paint_bitmap))
+ AddBitmap(paint_bitmap, mapped_rect);
+ }
+ virtual void drawBitmapRect(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkRect* src_or_null,
+ const SkRect& dst,
+ const SkPaint& paint) SK_OVERRIDE {
+ SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
+ SkMatrix matrix;
+ matrix.setRectToRect(bitmap_rect, dst, SkMatrix::kFill_ScaleToFit);
+ GatherPixelRefDevice::drawBitmap(draw, bitmap, matrix, paint);
+ }
+ virtual void drawSprite(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ int x,
+ int y,
+ const SkPaint& paint) SK_OVERRIDE {
+ // Sprites aren't affected by current matrix, so we can't reuse drawRect.
+ SkMatrix matrix;
+ matrix.setTranslate(x, y);
+
+ SkRect bitmap_rect = SkRect::MakeWH(bitmap.width(), bitmap.height());
+ SkRect mapped_rect;
+ matrix.mapRect(&mapped_rect, bitmap_rect);
+
+ AddBitmap(bitmap, mapped_rect);
+ SkBitmap paint_bitmap;
+ if (GetBitmapFromPaint(paint, &paint_bitmap))
+ AddBitmap(paint_bitmap, mapped_rect);
+ }
+ virtual void drawText(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) SK_OVERRIDE {
+ SkBitmap bitmap;
+ if (!GetBitmapFromPaint(paint, &bitmap))
+ return;
+
+ // Math is borrowed from SkBBoxRecord
+ SkRect bounds;
+ paint.measureText(text, len, &bounds);
+ SkPaint::FontMetrics metrics;
+ paint.getFontMetrics(&metrics);
+
+ if (paint.isVerticalText()) {
+ SkScalar h = bounds.fBottom - bounds.fTop;
+ if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+ bounds.fTop -= h / 2;
+ bounds.fBottom -= h / 2;
+ }
+ bounds.fBottom += metrics.fBottom;
+ bounds.fTop += metrics.fTop;
+ } else {
+ SkScalar w = bounds.fRight - bounds.fLeft;
+ if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+ bounds.fLeft -= w / 2;
+ bounds.fRight -= w / 2;
+ } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
+ bounds.fLeft -= w;
+ bounds.fRight -= w;
+ }
+ bounds.fTop = metrics.fTop;
+ bounds.fBottom = metrics.fBottom;
+ }
+
+ SkScalar pad = (metrics.fBottom - metrics.fTop) / 2;
+ bounds.fLeft -= pad;
+ bounds.fRight += pad;
+ bounds.fLeft += x;
+ bounds.fRight += x;
+ bounds.fTop += y;
+ bounds.fBottom += y;
+
+ GatherPixelRefDevice::drawRect(draw, bounds, paint);
+ }
+ virtual void drawPosText(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkScalar pos[],
+ SkScalar const_y,
+ int scalars_per_pos,
+ const SkPaint& paint) SK_OVERRIDE {
+ SkBitmap bitmap;
+ if (!GetBitmapFromPaint(paint, &bitmap))
+ return;
+
+ if (len == 0)
+ return;
+
+ // Similar to SkDraw asserts.
+ SkASSERT(scalars_per_pos == 1 || scalars_per_pos == 2);
+
+ SkPoint min_point;
+ SkPoint max_point;
+ if (scalars_per_pos == 1) {
+ min_point.set(pos[0], const_y);
+ max_point.set(pos[0], const_y);
+ } else if (scalars_per_pos == 2) {
+ min_point.set(pos[0], const_y + pos[1]);
+ max_point.set(pos[0], const_y + pos[1]);
+ }
+
+ for (size_t i = 0; i < len; ++i) {
+ SkScalar x = pos[i * scalars_per_pos];
+ SkScalar y = const_y;
+ if (scalars_per_pos == 2)
+ y += pos[i * scalars_per_pos + 1];
+
+ min_point.set(std::min(x, min_point.x()), std::min(y, min_point.y()));
+ max_point.set(std::max(x, max_point.x()), std::max(y, max_point.y()));
+ }
+
+ SkRect bounds = SkRect::MakeLTRB(
+ min_point.x(), min_point.y(), max_point.x(), max_point.y());
+
+ // Math is borrowed from SkBBoxRecord
+ SkPaint::FontMetrics metrics;
+ paint.getFontMetrics(&metrics);
+
+ bounds.fTop += metrics.fTop;
+ bounds.fBottom += metrics.fBottom;
+
+ SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
+ bounds.fLeft += pad;
+ bounds.fRight -= pad;
+
+ GatherPixelRefDevice::drawRect(draw, bounds, paint);
+ }
+ virtual void drawTextOnPath(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) SK_OVERRIDE {
+ SkBitmap bitmap;
+ if (!GetBitmapFromPaint(paint, &bitmap))
+ return;
+
+ // Math is borrowed from SkBBoxRecord
+ SkRect bounds = path.getBounds();
+ SkPaint::FontMetrics metrics;
+ paint.getFontMetrics(&metrics);
+
+ SkScalar pad = metrics.fTop;
+ bounds.fLeft += pad;
+ bounds.fRight -= pad;
+ bounds.fTop += pad;
+ bounds.fBottom -= pad;
+
+ GatherPixelRefDevice::drawRect(draw, bounds, paint);
+ }
+ virtual void drawVertices(const SkDraw& draw,
+ SkCanvas::VertexMode,
+ int vertex_count,
+ const SkPoint verts[],
+ const SkPoint texs[],
+ const SkColor colors[],
+ SkXfermode* xmode,
+ const uint16_t indices[],
+ int index_count,
+ const SkPaint& paint) SK_OVERRIDE {
+ GatherPixelRefDevice::drawPoints(
+ draw, SkCanvas::kPolygon_PointMode, vertex_count, verts, paint);
+ }
+ virtual void drawDevice(const SkDraw&,
+ SkDevice*,
+ int x,
+ int y,
+ const SkPaint&) SK_OVERRIDE {}
+
+ protected:
+ virtual bool onReadPixels(const SkBitmap& bitmap,
+ int x,
+ int y,
+ SkCanvas::Config8888 config8888) SK_OVERRIDE {
+ return false;
+ }
+
+ private:
+ LazyPixelRefSet* lazy_pixel_ref_set_;
+
+ void AddBitmap(const SkBitmap& bm, const SkRect& rect) {
+ lazy_pixel_ref_set_->Add(bm.pixelRef(), rect);
+ }
+
+ bool GetBitmapFromPaint(const SkPaint& paint, SkBitmap* bm) {
+ SkShader* shader = paint.getShader();
+ if (shader) {
+ // Check whether the shader is a gradient in order to prevent generation
+ // of bitmaps from gradient shaders, which implement asABitmap.
+ if (SkShader::kNone_GradientType == shader->asAGradient(NULL))
+ return shader->asABitmap(bm, NULL, NULL);
+ }
+ return false;
+ }
+};
+
+class NoSaveLayerCanvas : public SkCanvas {
+ public:
+ NoSaveLayerCanvas(SkDevice* device) : INHERITED(device) {}
+
+ // Turn saveLayer() into save() for speed, should not affect correctness.
+ virtual int saveLayer(const SkRect* bounds,
+ const SkPaint* paint,
+ SaveFlags flags) SK_OVERRIDE {
+
+ // Like SkPictureRecord, we don't want to create layers, but we do need
+ // to respect the save and (possibly) its rect-clip.
+ int count = this->INHERITED::save(flags);
+ if (bounds) {
+ this->INHERITED::clipRectBounds(bounds, flags, NULL);
+ }
+ return count;
+ }
+
+ // Disable aa for speed.
+ virtual bool clipRect(const SkRect& rect, SkRegion::Op op, bool doAA)
+ SK_OVERRIDE {
+ return this->INHERITED::clipRect(rect, op, false);
+ }
+
+ virtual bool clipPath(const SkPath& path, SkRegion::Op op, bool doAA)
+ SK_OVERRIDE {
+ return this->updateClipConservativelyUsingBounds(
+ path.getBounds(), op, path.isInverseFillType());
+ }
+ virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA)
+ SK_OVERRIDE {
+ return this->updateClipConservativelyUsingBounds(
+ rrect.getBounds(), op, false);
+ }
+
+ private:
+ typedef SkCanvas INHERITED;
+};
+
+} // namespace
+
+void LazyPixelRefUtils::GatherPixelRefs(
+ SkPicture* picture,
+ std::vector<PositionLazyPixelRef>* lazy_pixel_refs) {
+ lazy_pixel_refs->clear();
+ LazyPixelRefSet pixel_ref_set(lazy_pixel_refs);
+
+ SkBitmap empty_bitmap;
+ empty_bitmap.setConfig(
+ SkBitmap::kNo_Config, picture->width(), picture->height());
+
+ GatherPixelRefDevice device(empty_bitmap, &pixel_ref_set);
+ NoSaveLayerCanvas canvas(&device);
+
+ canvas.clipRect(SkRect::MakeWH(picture->width(), picture->height()),
+ SkRegion::kIntersect_Op,
+ false);
+ canvas.drawPicture(*picture);
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/lazy_pixel_ref_utils.h b/chromium/skia/ext/lazy_pixel_ref_utils.h
new file mode 100644
index 00000000000..33fe039e1ac
--- /dev/null
+++ b/chromium/skia/ext/lazy_pixel_ref_utils.h
@@ -0,0 +1,33 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_LAZY_PIXEL_REF_UTILS_H_
+#define SKIA_EXT_LAZY_PIXEL_REF_UTILS_H_
+
+#include <vector>
+
+#include "SkPicture.h"
+#include "SkRect.h"
+
+namespace skia {
+
+class LazyPixelRef;
+class SK_API LazyPixelRefUtils {
+ public:
+
+ struct PositionLazyPixelRef {
+ skia::LazyPixelRef* lazy_pixel_ref;
+ SkRect pixel_ref_rect;
+ };
+
+ static void GatherPixelRefs(
+ SkPicture* picture,
+ std::vector<PositionLazyPixelRef>* lazy_pixel_refs);
+};
+
+typedef std::vector<LazyPixelRefUtils::PositionLazyPixelRef> LazyPixelRefList;
+
+} // namespace skia
+
+#endif
diff --git a/chromium/skia/ext/lazy_pixel_ref_utils_unittest.cc b/chromium/skia/ext/lazy_pixel_ref_utils_unittest.cc
new file mode 100644
index 00000000000..a9f60a23ab1
--- /dev/null
+++ b/chromium/skia/ext/lazy_pixel_ref_utils_unittest.cc
@@ -0,0 +1,735 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "cc/test/geometry_test_utils.h"
+#include "skia/ext/lazy_pixel_ref.h"
+#include "skia/ext/lazy_pixel_ref_utils.h"
+#include "skia/ext/refptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkFlattenableBuffers.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "third_party/skia/include/core/SkShader.h"
+#include "third_party/skia/src/core/SkOrderedReadBuffer.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/skia_util.h"
+
+namespace skia {
+
+namespace {
+
+void CreateBitmap(gfx::Size size, const char* uri, SkBitmap* bitmap);
+
+class TestPixelRef : public SkPixelRef {
+ public:
+ TestPixelRef(int width, int height);
+ virtual ~TestPixelRef();
+
+ virtual SkFlattenable::Factory getFactory() OVERRIDE;
+ virtual void* onLockPixels(SkColorTable** color_table) OVERRIDE;
+ virtual void onUnlockPixels() OVERRIDE {}
+ virtual SkPixelRef* deepCopy(SkBitmap::Config config, const SkIRect* subset)
+ OVERRIDE;
+
+ private:
+ scoped_ptr<char[]> pixels_;
+};
+
+class TestLazyPixelRef : public skia::LazyPixelRef {
+ public:
+ TestLazyPixelRef(int width, int height);
+ virtual ~TestLazyPixelRef();
+
+ virtual SkFlattenable::Factory getFactory() OVERRIDE;
+ virtual void* onLockPixels(SkColorTable** color_table) OVERRIDE;
+ virtual void onUnlockPixels() OVERRIDE {}
+ virtual bool PrepareToDecode(const PrepareParams& params) OVERRIDE;
+ virtual bool MaybeDecoded() OVERRIDE;
+ virtual SkPixelRef* deepCopy(SkBitmap::Config config, const SkIRect* subset)
+ OVERRIDE;
+ virtual void Decode() OVERRIDE {}
+
+ private:
+ scoped_ptr<char[]> pixels_;
+};
+
+class TestLazyShader : public SkShader {
+ public:
+ TestLazyShader() { CreateBitmap(gfx::Size(50, 50), "lazy", &bitmap_); }
+
+ TestLazyShader(SkFlattenableReadBuffer& flattenable_buffer) {
+ SkOrderedReadBuffer& buffer =
+ static_cast<SkOrderedReadBuffer&>(flattenable_buffer);
+ SkReader32* reader = buffer.getReader32();
+
+ reader->skip(-4);
+ uint32_t toSkip = reader->readU32();
+ reader->skip(toSkip);
+
+ CreateBitmap(gfx::Size(50, 50), "lazy", &bitmap_);
+ }
+
+ virtual SkShader::BitmapType asABitmap(SkBitmap* bitmap,
+ SkMatrix* matrix,
+ TileMode xy[2]) const OVERRIDE {
+ if (bitmap)
+ *bitmap = bitmap_;
+ return SkShader::kDefault_BitmapType;
+ }
+
+ // Pure virtual implementaiton.
+ virtual void shadeSpan(int x, int y, SkPMColor[], int count) OVERRIDE {}
+ SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(TestLazyShader);
+
+ private:
+ SkBitmap bitmap_;
+};
+
+TestPixelRef::TestPixelRef(int width, int height)
+ : pixels_(new char[4 * width * height]) {}
+
+TestPixelRef::~TestPixelRef() {}
+
+SkFlattenable::Factory TestPixelRef::getFactory() { return NULL; }
+
+void* TestPixelRef::onLockPixels(SkColorTable** color_table) {
+ return pixels_.get();
+}
+
+SkPixelRef* TestPixelRef::deepCopy(SkBitmap::Config config,
+ const SkIRect* subset) {
+ this->ref();
+ return this;
+}
+
+TestLazyPixelRef::TestLazyPixelRef(int width, int height)
+ : pixels_(new char[4 * width * height]) {}
+
+TestLazyPixelRef::~TestLazyPixelRef() {}
+
+SkFlattenable::Factory TestLazyPixelRef::getFactory() { return NULL; }
+
+void* TestLazyPixelRef::onLockPixels(SkColorTable** color_table) {
+ return pixels_.get();
+}
+
+bool TestLazyPixelRef::PrepareToDecode(const PrepareParams& params) {
+ return true;
+}
+
+bool TestLazyPixelRef::MaybeDecoded() {
+ return true;
+}
+
+SkPixelRef* TestLazyPixelRef::deepCopy(SkBitmap::Config config,
+ const SkIRect* subset) {
+ this->ref();
+ return this;
+}
+
+void CreateBitmap(gfx::Size size, const char* uri, SkBitmap* bitmap) {
+ skia::RefPtr<TestLazyPixelRef> lazy_pixel_ref =
+ skia::AdoptRef(new TestLazyPixelRef(size.width(), size.height()));
+ lazy_pixel_ref->setURI(uri);
+
+ bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+ bitmap->setPixelRef(lazy_pixel_ref.get());
+}
+
+SkCanvas* StartRecording(SkPicture* picture, gfx::Rect layer_rect) {
+ SkCanvas* canvas = picture->beginRecording(
+ layer_rect.width(),
+ layer_rect.height(),
+ SkPicture::kUsePathBoundsForClip_RecordingFlag |
+ SkPicture::kOptimizeForClippedPlayback_RecordingFlag);
+
+ canvas->save();
+ canvas->translate(-layer_rect.x(), -layer_rect.y());
+ canvas->clipRect(SkRect::MakeXYWH(
+ layer_rect.x(), layer_rect.y(), layer_rect.width(), layer_rect.height()));
+
+ return canvas;
+}
+
+void StopRecording(SkPicture* picture, SkCanvas* canvas) {
+ canvas->restore();
+ picture->endRecording();
+}
+
+} // namespace
+
+TEST(LazyPixelRefUtilsTest, DrawPaint) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ TestLazyShader second_shader;
+ SkPaint second_paint;
+ second_paint.setShader(&second_shader);
+
+ TestLazyShader third_shader;
+ SkPaint third_paint;
+ third_paint.setShader(&third_shader);
+
+ canvas->drawPaint(first_paint);
+ canvas->clipRect(SkRect::MakeXYWH(34, 45, 56, 67));
+ canvas->drawPaint(second_paint);
+ // Total clip is now (34, 45, 56, 55)
+ canvas->clipRect(SkRect::MakeWH(100, 100));
+ canvas->drawPaint(third_paint);
+
+ StopRecording(picture.get(), canvas);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(3u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 256, 256),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(34, 45, 56, 67),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(34, 45, 56, 55),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+}
+
+TEST(LazyPixelRefUtilsTest, DrawPoints) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ TestLazyShader second_shader;
+ SkPaint second_paint;
+ second_paint.setShader(&second_shader);
+
+ TestLazyShader third_shader;
+ SkPaint third_paint;
+ third_paint.setShader(&third_shader);
+
+ SkPoint points[3];
+ points[0].set(10, 10);
+ points[1].set(100, 20);
+ points[2].set(50, 100);
+ // (10, 10, 90, 90).
+ canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, points, first_paint);
+
+ canvas->save();
+
+ canvas->clipRect(SkRect::MakeWH(50, 50));
+ // (10, 10, 40, 40).
+ canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, points, second_paint);
+
+ canvas->restore();
+
+ points[0].set(50, 55);
+ points[1].set(50, 55);
+ points[2].set(200, 200);
+ // (50, 55, 150, 145).
+ canvas->drawPoints(SkCanvas::kPolygon_PointMode, 3, points, third_paint);
+
+ StopRecording(picture.get(), canvas);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(3u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(10, 10, 90, 90),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(10, 10, 40, 40),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(50, 55, 150, 145),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+}
+
+TEST(LazyPixelRefUtilsTest, DrawRect) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ TestLazyShader second_shader;
+ SkPaint second_paint;
+ second_paint.setShader(&second_shader);
+
+ TestLazyShader third_shader;
+ SkPaint third_paint;
+ third_paint.setShader(&third_shader);
+
+ // (10, 20, 30, 40).
+ canvas->drawRect(SkRect::MakeXYWH(10, 20, 30, 40), first_paint);
+
+ canvas->save();
+
+ canvas->translate(5, 17);
+ // (5, 50, 25, 35)
+ canvas->drawRect(SkRect::MakeXYWH(0, 33, 25, 35), second_paint);
+
+ canvas->restore();
+
+ canvas->clipRect(SkRect::MakeXYWH(50, 50, 50, 50));
+ canvas->translate(20, 20);
+ // (50, 50, 50, 50)
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, 100, 100), third_paint);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(3u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(10, 20, 30, 40),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(5, 50, 25, 35),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(50, 50, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+}
+
+TEST(LazyPixelRefUtilsTest, DrawRRect) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ TestLazyShader second_shader;
+ SkPaint second_paint;
+ second_paint.setShader(&second_shader);
+
+ TestLazyShader third_shader;
+ SkPaint third_paint;
+ third_paint.setShader(&third_shader);
+
+ SkRRect rrect;
+ rrect.setRect(SkRect::MakeXYWH(10, 20, 30, 40));
+
+ // (10, 20, 30, 40).
+ canvas->drawRRect(rrect, first_paint);
+
+ canvas->save();
+
+ canvas->translate(5, 17);
+ rrect.setRect(SkRect::MakeXYWH(0, 33, 25, 35));
+ // (5, 50, 25, 35)
+ canvas->drawRRect(rrect, second_paint);
+
+ canvas->restore();
+
+ canvas->clipRect(SkRect::MakeXYWH(50, 50, 50, 50));
+ canvas->translate(20, 20);
+ rrect.setRect(SkRect::MakeXYWH(0, 0, 100, 100));
+ // (50, 50, 50, 50)
+ canvas->drawRRect(rrect, third_paint);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(3u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(10, 20, 30, 40),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(5, 50, 25, 35),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(50, 50, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+}
+
+TEST(LazyPixelRefUtilsTest, DrawOval) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ TestLazyShader second_shader;
+ SkPaint second_paint;
+ second_paint.setShader(&second_shader);
+
+ TestLazyShader third_shader;
+ SkPaint third_paint;
+ third_paint.setShader(&third_shader);
+
+ canvas->save();
+
+ canvas->scale(2, 0.5);
+ // (20, 10, 60, 20).
+ canvas->drawOval(SkRect::MakeXYWH(10, 20, 30, 40), first_paint);
+
+ canvas->restore();
+ canvas->save();
+
+ canvas->translate(1, 2);
+ // (1, 35, 25, 35)
+ canvas->drawRect(SkRect::MakeXYWH(0, 33, 25, 35), second_paint);
+
+ canvas->restore();
+
+ canvas->clipRect(SkRect::MakeXYWH(50, 50, 50, 50));
+ canvas->translate(20, 20);
+ // (50, 50, 50, 50)
+ canvas->drawRect(SkRect::MakeXYWH(0, 0, 100, 100), third_paint);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(3u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(20, 10, 60, 20),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(1, 35, 25, 35),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(50, 50, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+}
+
+TEST(LazyPixelRefUtilsTest, DrawPath) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ TestLazyShader second_shader;
+ SkPaint second_paint;
+ second_paint.setShader(&second_shader);
+
+ SkPath path;
+ path.moveTo(12, 13);
+ path.lineTo(50, 50);
+ path.lineTo(22, 101);
+
+ // (12, 13, 38, 88).
+ canvas->drawPath(path, first_paint);
+
+ canvas->save();
+ canvas->clipRect(SkRect::MakeWH(50, 50));
+
+ // (12, 13, 38, 37).
+ canvas->drawPath(path, second_paint);
+
+ canvas->restore();
+
+ StopRecording(picture.get(), canvas);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(2u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(12, 13, 38, 88),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(12, 13, 38, 37),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+}
+
+TEST(LazyPixelRefUtilsTest, DrawBitmap) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ SkBitmap first;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &first);
+ SkBitmap second;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &second);
+ SkBitmap third;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &third);
+ SkBitmap fourth;
+ CreateBitmap(gfx::Size(50, 1), "lazy", &fourth);
+ SkBitmap fifth;
+ CreateBitmap(gfx::Size(10, 10), "lazy", &fifth);
+
+ canvas->save();
+
+ // At (0, 0).
+ canvas->drawBitmap(first, 0, 0);
+ canvas->translate(25, 0);
+ // At (25, 0).
+ canvas->drawBitmap(second, 0, 0);
+ canvas->translate(0, 50);
+ // At (50, 50).
+ canvas->drawBitmap(third, 25, 0);
+
+ canvas->restore();
+ canvas->save();
+
+ canvas->rotate(90);
+ // At (0, 0), rotated 90 degrees
+ canvas->drawBitmap(fourth, 0, 0);
+
+ canvas->restore();
+
+ canvas->scale(5, 6);
+ // At (0, 0), scaled by 5 and 6
+ canvas->drawBitmap(fifth, 0, 0);
+
+ StopRecording(picture.get(), canvas);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(5u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(25, 0, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(50, 50, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(-1, 0, 1, 50),
+ gfx::SkRectToRectF(pixel_refs[3].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 50, 60),
+ gfx::SkRectToRectF(pixel_refs[4].pixel_ref_rect));
+
+}
+
+TEST(LazyPixelRefUtilsTest, DrawBitmapRect) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ SkBitmap first;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &first);
+ SkBitmap second;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &second);
+ SkBitmap third;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &third);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ SkPaint non_lazy_paint;
+
+ canvas->save();
+
+ // (0, 0, 100, 100).
+ canvas->drawBitmapRect(first, SkRect::MakeWH(100, 100), &non_lazy_paint);
+ canvas->translate(25, 0);
+ // (75, 50, 10, 10).
+ canvas->drawBitmapRect(
+ second, SkRect::MakeXYWH(50, 50, 10, 10), &non_lazy_paint);
+ canvas->translate(5, 50);
+ // (0, 30, 100, 100). One from bitmap, one from paint.
+ canvas->drawBitmapRect(
+ third, SkRect::MakeXYWH(-30, -20, 100, 100), &first_paint);
+
+ canvas->restore();
+
+ StopRecording(picture.get(), canvas);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(4u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 100, 100),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(75, 50, 10, 10),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 30, 100, 100),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 30, 100, 100),
+ gfx::SkRectToRectF(pixel_refs[3].pixel_ref_rect));
+}
+
+TEST(LazyPixelRefUtilsTest, DrawSprite) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ SkBitmap first;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &first);
+ SkBitmap second;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &second);
+ SkBitmap third;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &third);
+ SkBitmap fourth;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &fourth);
+ SkBitmap fifth;
+ CreateBitmap(gfx::Size(50, 50), "lazy", &fifth);
+
+ canvas->save();
+
+ // Sprites aren't affected by the current matrix.
+
+ // (0, 0, 50, 50).
+ canvas->drawSprite(first, 0, 0);
+ canvas->translate(25, 0);
+ // (10, 0, 50, 50).
+ canvas->drawSprite(second, 10, 0);
+ canvas->translate(0, 50);
+ // (25, 0, 50, 50).
+ canvas->drawSprite(third, 25, 0);
+
+ canvas->restore();
+ canvas->save();
+
+ canvas->rotate(90);
+ // (0, 0, 50, 50).
+ canvas->drawSprite(fourth, 0, 0);
+
+ canvas->restore();
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ canvas->scale(5, 6);
+ // (100, 100, 50, 50).
+ canvas->drawSprite(fifth, 100, 100, &first_paint);
+
+ StopRecording(picture.get(), canvas);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(6u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(10, 0, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(25, 0, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(0, 0, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[3].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(100, 100, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[4].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(100, 100, 50, 50),
+ gfx::SkRectToRectF(pixel_refs[5].pixel_ref_rect));
+}
+
+TEST(LazyPixelRefUtilsTest, DrawText) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ SkPoint points[4];
+ points[0].set(10, 50);
+ points[1].set(20, 50);
+ points[2].set(30, 50);
+ points[3].set(40, 50);
+
+ SkPath path;
+ path.moveTo(10, 50);
+ path.lineTo(20, 50);
+ path.lineTo(30, 50);
+ path.lineTo(40, 50);
+ path.lineTo(50, 50);
+
+ canvas->drawText("text", 4, 50, 50, first_paint);
+ canvas->drawPosText("text", 4, points, first_paint);
+ canvas->drawTextOnPath("text", 4, path, NULL, first_paint);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(3u, pixel_refs.size());
+}
+
+TEST(LazyPixelRefUtilsTest, DrawVertices) {
+ gfx::Rect layer_rect(0, 0, 256, 256);
+
+ skia::RefPtr<SkPicture> picture = skia::AdoptRef(new SkPicture);
+ SkCanvas* canvas = StartRecording(picture.get(), layer_rect);
+
+ TestLazyShader first_shader;
+ SkPaint first_paint;
+ first_paint.setShader(&first_shader);
+
+ TestLazyShader second_shader;
+ SkPaint second_paint;
+ second_paint.setShader(&second_shader);
+
+ TestLazyShader third_shader;
+ SkPaint third_paint;
+ third_paint.setShader(&third_shader);
+
+ SkPoint points[3];
+ SkColor colors[3];
+ uint16_t indecies[3] = {0, 1, 2};
+ points[0].set(10, 10);
+ points[1].set(100, 20);
+ points[2].set(50, 100);
+ // (10, 10, 90, 90).
+ canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
+ 3,
+ points,
+ points,
+ colors,
+ NULL,
+ indecies,
+ 3,
+ first_paint);
+
+ canvas->save();
+
+ canvas->clipRect(SkRect::MakeWH(50, 50));
+ // (10, 10, 40, 40).
+ canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
+ 3,
+ points,
+ points,
+ colors,
+ NULL,
+ indecies,
+ 3,
+ second_paint);
+
+ canvas->restore();
+
+ points[0].set(50, 55);
+ points[1].set(50, 55);
+ points[2].set(200, 200);
+ // (50, 55, 150, 145).
+ canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
+ 3,
+ points,
+ points,
+ colors,
+ NULL,
+ indecies,
+ 3,
+ third_paint);
+
+ StopRecording(picture.get(), canvas);
+
+ std::vector<skia::LazyPixelRefUtils::PositionLazyPixelRef> pixel_refs;
+ skia::LazyPixelRefUtils::GatherPixelRefs(picture.get(), &pixel_refs);
+
+ EXPECT_EQ(3u, pixel_refs.size());
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(10, 10, 90, 90),
+ gfx::SkRectToRectF(pixel_refs[0].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(10, 10, 40, 40),
+ gfx::SkRectToRectF(pixel_refs[1].pixel_ref_rect));
+ EXPECT_FLOAT_RECT_EQ(gfx::RectF(50, 55, 150, 145),
+ gfx::SkRectToRectF(pixel_refs[2].pixel_ref_rect));
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/paint_simplifier.cc b/chromium/skia/ext/paint_simplifier.cc
new file mode 100644
index 00000000000..6b697662458
--- /dev/null
+++ b/chromium/skia/ext/paint_simplifier.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/paint_simplifier.h"
+#include "third_party/skia/include/core/SkPaint.h"
+
+namespace skia {
+
+PaintSimplifier::PaintSimplifier()
+ : INHERITED() {
+
+}
+
+PaintSimplifier::~PaintSimplifier() {
+
+}
+
+bool PaintSimplifier::filter(SkPaint* paint, Type type) {
+
+ // Preserve a modicum of text quality; black & white text is
+ // just too blocky, even during a fling.
+ if (type != kText_Type) {
+ paint->setAntiAlias(false);
+ }
+ paint->setSubpixelText(false);
+ paint->setLCDRenderText(false);
+
+ paint->setFilterBitmap(false);
+ paint->setMaskFilter(NULL);
+
+ // Uncomment this line to shade simplified tiles pink during debugging.
+ //paint->setColor(SkColorSetRGB(255, 127, 127));
+
+ return true;
+}
+
+
+} // namespace skia
+
+
diff --git a/chromium/skia/ext/paint_simplifier.h b/chromium/skia/ext/paint_simplifier.h
new file mode 100644
index 00000000000..0eff833727d
--- /dev/null
+++ b/chromium/skia/ext/paint_simplifier.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_PAINT_SIMPLIFIER_H
+#define SKIA_EXT_PAINT_SIMPLIFIER_H
+
+#include "base/values.h"
+#include "third_party/skia/include/core/SkDrawFilter.h"
+
+class SkPaint;
+
+namespace skia {
+
+/*
+ When installed on a SkCanvas, reduces the quality of all draws
+ to that canvas. This improves rasterization speed during flings.
+ We turn off blurs, filters, and antialiasing *except for* text.
+*/
+class SK_API PaintSimplifier : public SkDrawFilter {
+ public:
+ PaintSimplifier();
+ virtual ~PaintSimplifier();
+ virtual bool filter(SkPaint*, Type) OVERRIDE;
+
+ private:
+ typedef SkDrawFilter INHERITED;
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_PAINT_SIMPLIFIER_H
+
diff --git a/chromium/skia/ext/platform_canvas.cc b/chromium/skia/ext/platform_canvas.cc
new file mode 100644
index 00000000000..6a2548100bc
--- /dev/null
+++ b/chromium/skia/ext/platform_canvas.cc
@@ -0,0 +1,78 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/platform_canvas.h"
+
+#include "skia/ext/bitmap_platform_device.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+namespace skia {
+
+SkDevice* GetTopDevice(const SkCanvas& canvas) {
+ return canvas.getTopDevice(true);
+}
+
+bool SupportsPlatformPaint(const SkCanvas* canvas) {
+ PlatformDevice* platform_device = GetPlatformDevice(GetTopDevice(*canvas));
+ return platform_device && platform_device->SupportsPlatformPaint();
+}
+
+PlatformSurface BeginPlatformPaint(SkCanvas* canvas) {
+ PlatformDevice* platform_device = GetPlatformDevice(GetTopDevice(*canvas));
+ if (platform_device)
+ return platform_device->BeginPlatformPaint();
+
+ return 0;
+}
+
+void EndPlatformPaint(SkCanvas* canvas) {
+ PlatformDevice* platform_device = GetPlatformDevice(GetTopDevice(*canvas));
+ if (platform_device)
+ platform_device->EndPlatformPaint();
+}
+
+void DrawToNativeContext(SkCanvas* canvas, PlatformSurface context, int x,
+ int y, const PlatformRect* src_rect) {
+ PlatformDevice* platform_device = GetPlatformDevice(GetTopDevice(*canvas));
+ if (platform_device)
+ platform_device->DrawToNativeContext(context, x, y, src_rect);
+}
+
+static SkPMColor MakeOpaqueXfermodeProc(SkPMColor src, SkPMColor dst) {
+ return dst | (0xFF << SK_A32_SHIFT);
+}
+
+void MakeOpaque(SkCanvas* canvas, int x, int y, int width, int height) {
+ if (width <= 0 || height <= 0)
+ return;
+
+ SkRect rect;
+ rect.setXYWH(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(width), SkIntToScalar(height));
+ SkPaint paint;
+ // so we don't draw anything on a device that ignores xfermodes
+ paint.setColor(0);
+ // install our custom mode
+ skia::RefPtr<SkProcXfermode> xfermode =
+ skia::AdoptRef(new SkProcXfermode(MakeOpaqueXfermodeProc));
+ paint.setXfermode(xfermode.get());
+ canvas->drawRect(rect, paint);
+}
+
+size_t PlatformCanvasStrideForWidth(unsigned width) {
+ return 4 * width;
+}
+
+SkCanvas* CreateCanvas(const skia::RefPtr<SkDevice>& device, OnFailureType failureType) {
+ if (!device) {
+ if (CRASH_ON_FAILURE == failureType)
+ SK_CRASH();
+ return NULL;
+ }
+ return new SkCanvas(device.get());
+}
+
+PlatformBitmap::PlatformBitmap() : surface_(0), platform_extra_(0) {}
+
+} // namespace skia
diff --git a/chromium/skia/ext/platform_canvas.h b/chromium/skia/ext/platform_canvas.h
new file mode 100644
index 00000000000..9e2bc823794
--- /dev/null
+++ b/chromium/skia/ext/platform_canvas.h
@@ -0,0 +1,188 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_PLATFORM_CANVAS_H_
+#define SKIA_EXT_PLATFORM_CANVAS_H_
+
+// The platform-specific device will include the necessary platform headers
+// to get the surface type.
+#include "base/basictypes.h"
+#include "skia/ext/platform_device.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
+
+namespace skia {
+
+typedef SkCanvas PlatformCanvas;
+
+//
+// Note about error handling.
+//
+// Creating a canvas can fail at times, most often because we fail to allocate
+// the backing-store (pixels). This can be from out-of-memory, or something
+// more opaque, like GDI or cairo reported a failure.
+//
+// To allow the caller to handle the failure, every Create... factory takes an
+// enum as its last parameter. The default value is kCrashOnFailure. If the
+// caller passes kReturnNullOnFailure, then the caller is responsible to check
+// the return result.
+//
+enum OnFailureType {
+ CRASH_ON_FAILURE,
+ RETURN_NULL_ON_FAILURE
+};
+
+#if defined(WIN32)
+ // The shared_section parameter is passed to gfx::PlatformDevice::create.
+ // See it for details.
+ SK_API SkCanvas* CreatePlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section,
+ OnFailureType failure_type);
+#elif defined(__APPLE__)
+ SK_API SkCanvas* CreatePlatformCanvas(CGContextRef context,
+ int width,
+ int height,
+ bool is_opaque,
+ OnFailureType failure_type);
+
+ SK_API SkCanvas* CreatePlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ uint8_t* context,
+ OnFailureType failure_type);
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
+ defined(__sun) || defined(ANDROID)
+ // Linux ---------------------------------------------------------------------
+
+ // Construct a canvas from the given memory region. The memory is not cleared
+ // first. @data must be, at least, @height * StrideForWidth(@width) bytes.
+ SK_API SkCanvas* CreatePlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ uint8_t* data,
+ OnFailureType failure_type);
+#endif
+
+static inline SkCanvas* CreatePlatformCanvas(int width,
+ int height,
+ bool is_opaque) {
+ return CreatePlatformCanvas(width, height, is_opaque, 0, CRASH_ON_FAILURE);
+}
+
+SK_API SkCanvas* CreateCanvas(const skia::RefPtr<SkDevice>& device,
+ OnFailureType failure_type);
+
+static inline SkCanvas* CreateBitmapCanvas(int width,
+ int height,
+ bool is_opaque) {
+ return CreatePlatformCanvas(width, height, is_opaque, 0, CRASH_ON_FAILURE);
+}
+
+static inline SkCanvas* TryCreateBitmapCanvas(int width,
+ int height,
+ bool is_opaque) {
+ return CreatePlatformCanvas(width, height, is_opaque, 0,
+ RETURN_NULL_ON_FAILURE);
+}
+
+// Return the stride (length of a line in bytes) for the given width. Because
+// we use 32-bits per pixel, this will be roughly 4*width. However, for
+// alignment reasons we may wish to increase that.
+SK_API size_t PlatformCanvasStrideForWidth(unsigned width);
+
+// Returns the SkDevice pointer of the topmost rect with a non-empty
+// clip. In practice, this is usually either the top layer or nothing, since
+// we usually set the clip to new layers when we make them.
+//
+// This may return NULL, so callers need to check.
+//
+// This is different than SkCanvas' getDevice, because that returns the
+// bottommost device.
+//
+// Danger: the resulting device should not be saved. It will be invalidated
+// by the next call to save() or restore().
+SK_API SkDevice* GetTopDevice(const SkCanvas& canvas);
+
+// Returns true if native platform routines can be used to draw on the
+// given canvas. If this function returns false, BeginPlatformPaint will
+// return NULL PlatformSurface.
+SK_API bool SupportsPlatformPaint(const SkCanvas* canvas);
+
+// Draws into the a native platform surface, |context|. Forwards to
+// DrawToNativeContext on a PlatformDevice instance bound to the top device.
+// If no PlatformDevice instance is bound, is a no-operation.
+SK_API void DrawToNativeContext(SkCanvas* canvas,
+ PlatformSurface context,
+ int x,
+ int y,
+ const PlatformRect* src_rect);
+
+// Sets the opacity of each pixel in the specified region to be opaque.
+SK_API void MakeOpaque(SkCanvas* canvas, int x, int y, int width, int height);
+
+// These calls should surround calls to platform drawing routines, the
+// surface returned here can be used with the native platform routines.
+//
+// Call EndPlatformPaint when you are done and want to use skia operations
+// after calling the platform-specific BeginPlatformPaint; this will
+// synchronize the bitmap to OS if necessary.
+SK_API PlatformSurface BeginPlatformPaint(SkCanvas* canvas);
+SK_API void EndPlatformPaint(SkCanvas* canvas);
+
+// Helper class for pairing calls to BeginPlatformPaint and EndPlatformPaint.
+// Upon construction invokes BeginPlatformPaint, and upon destruction invokes
+// EndPlatformPaint.
+class SK_API ScopedPlatformPaint {
+ public:
+ explicit ScopedPlatformPaint(SkCanvas* canvas) : canvas_(canvas) {
+ platform_surface_ = BeginPlatformPaint(canvas);
+ }
+ ~ScopedPlatformPaint() { EndPlatformPaint(canvas_); }
+
+ // Returns the PlatformSurface to use for native platform drawing calls.
+ PlatformSurface GetPlatformSurface() { return platform_surface_; }
+ private:
+ SkCanvas* canvas_;
+ PlatformSurface platform_surface_;
+
+ // Disallow copy and assign
+ ScopedPlatformPaint(const ScopedPlatformPaint&);
+ ScopedPlatformPaint& operator=(const ScopedPlatformPaint&);
+};
+
+// PlatformBitmap holds a PlatformSurface that can also be used as an SkBitmap.
+class SK_API PlatformBitmap {
+ public:
+ PlatformBitmap();
+ ~PlatformBitmap();
+
+ // Returns true if the bitmap was able to allocate its surface.
+ bool Allocate(int width, int height, bool is_opaque);
+
+ // Returns the platform surface, or 0 if Allocate() did not return true.
+ PlatformSurface GetSurface() { return surface_; }
+
+ // Return the skia bitmap, which will be empty if Allocate() did not
+ // return true.
+ //
+ // The resulting SkBitmap holds a refcount on the underlying platform surface,
+ // so the surface will remain allocated so long as the SkBitmap or its copies
+ // stay around.
+ const SkBitmap& GetBitmap() { return bitmap_; }
+
+ private:
+ SkBitmap bitmap_;
+ PlatformSurface surface_; // initialized to 0
+ intptr_t platform_extra_; // platform specific, initialized to 0
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformBitmap);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_PLATFORM_CANVAS_H_
diff --git a/chromium/skia/ext/platform_canvas_unittest.cc b/chromium/skia/ext/platform_canvas_unittest.cc
new file mode 100644
index 00000000000..7595bcaca2b
--- /dev/null
+++ b/chromium/skia/ext/platform_canvas_unittest.cc
@@ -0,0 +1,459 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// TODO(awalker): clean up the const/non-const reference handling in this test
+
+#include "build/build_config.h"
+
+#if defined(OS_MACOSX)
+#import <ApplicationServices/ApplicationServices.h>
+#endif
+
+#if !defined(OS_WIN)
+#include <unistd.h>
+#endif
+
+#include "base/memory/scoped_ptr.h"
+#include "skia/ext/platform_canvas.h"
+#include "skia/ext/platform_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkPixelRef.h"
+
+namespace skia {
+
+namespace {
+
+// Return true if the canvas is filled to canvas_color, and contains a single
+// rectangle filled to rect_color. This function ignores the alpha channel,
+// since Windows will sometimes clear the alpha channel when drawing, and we
+// will fix that up later in cases it's necessary.
+bool VerifyRect(const PlatformCanvas& canvas,
+ uint32_t canvas_color, uint32_t rect_color,
+ int x, int y, int w, int h) {
+ SkDevice* device = skia::GetTopDevice(canvas);
+ const SkBitmap& bitmap = device->accessBitmap(false);
+ SkAutoLockPixels lock(bitmap);
+
+ // For masking out the alpha values.
+ uint32_t alpha_mask = 0xFF << SK_A32_SHIFT;
+
+ for (int cur_y = 0; cur_y < bitmap.height(); cur_y++) {
+ for (int cur_x = 0; cur_x < bitmap.width(); cur_x++) {
+ if (cur_x >= x && cur_x < x + w &&
+ cur_y >= y && cur_y < y + h) {
+ // Inside the square should be rect_color
+ if ((*bitmap.getAddr32(cur_x, cur_y) | alpha_mask) !=
+ (rect_color | alpha_mask))
+ return false;
+ } else {
+ // Outside the square should be canvas_color
+ if ((*bitmap.getAddr32(cur_x, cur_y) | alpha_mask) !=
+ (canvas_color | alpha_mask))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+#if !defined(OS_MACOSX)
+bool IsOfColor(const SkBitmap& bitmap, int x, int y, uint32_t color) {
+ // For masking out the alpha values.
+ static uint32_t alpha_mask = 0xFF << SK_A32_SHIFT;
+ return (*bitmap.getAddr32(x, y) | alpha_mask) == (color | alpha_mask);
+}
+
+// Return true if canvas has something that passes for a rounded-corner
+// rectangle. Basically, we're just checking to make sure that the pixels in the
+// middle are of rect_color and pixels in the corners are of canvas_color.
+bool VerifyRoundedRect(const PlatformCanvas& canvas,
+ uint32_t canvas_color, uint32_t rect_color,
+ int x, int y, int w, int h) {
+ SkDevice* device = skia::GetTopDevice(canvas);
+ const SkBitmap& bitmap = device->accessBitmap(false);
+ SkAutoLockPixels lock(bitmap);
+
+ // Check corner points first. They should be of canvas_color.
+ if (!IsOfColor(bitmap, x, y, canvas_color)) return false;
+ if (!IsOfColor(bitmap, x + w, y, canvas_color)) return false;
+ if (!IsOfColor(bitmap, x, y + h, canvas_color)) return false;
+ if (!IsOfColor(bitmap, x + w, y, canvas_color)) return false;
+
+ // Check middle points. They should be of rect_color.
+ if (!IsOfColor(bitmap, (x + w / 2), y, rect_color)) return false;
+ if (!IsOfColor(bitmap, x, (y + h / 2), rect_color)) return false;
+ if (!IsOfColor(bitmap, x + w, (y + h / 2), rect_color)) return false;
+ if (!IsOfColor(bitmap, (x + w / 2), y + h, rect_color)) return false;
+
+ return true;
+}
+#endif
+
+// Checks whether there is a white canvas with a black square at the given
+// location in pixels (not in the canvas coordinate system).
+bool VerifyBlackRect(const PlatformCanvas& canvas, int x, int y, int w, int h) {
+ return VerifyRect(canvas, SK_ColorWHITE, SK_ColorBLACK, x, y, w, h);
+}
+
+// Check that every pixel in the canvas is a single color.
+bool VerifyCanvasColor(const PlatformCanvas& canvas, uint32_t canvas_color) {
+ return VerifyRect(canvas, canvas_color, 0, 0, 0, 0, 0);
+}
+
+#if defined(OS_WIN)
+void DrawNativeRect(PlatformCanvas& canvas, int x, int y, int w, int h) {
+ skia::ScopedPlatformPaint scoped_platform_paint(&canvas);
+ HDC dc = scoped_platform_paint.GetPlatformSurface();
+
+ RECT inner_rc;
+ inner_rc.left = x;
+ inner_rc.top = y;
+ inner_rc.right = x + w;
+ inner_rc.bottom = y + h;
+ FillRect(dc, &inner_rc, reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH)));
+}
+#elif defined(OS_MACOSX)
+void DrawNativeRect(PlatformCanvas& canvas, int x, int y, int w, int h) {
+ skia::ScopedPlatformPaint scoped_platform_paint(&canvas);
+ CGContextRef context = scoped_platform_paint.GetPlatformSurface();
+
+ CGRect inner_rc = CGRectMake(x, y, w, h);
+ // RGBA opaque black
+ CGColorRef black = CGColorCreateGenericRGB(0.0, 0.0, 0.0, 1.0);
+ CGContextSetFillColorWithColor(context, black);
+ CGColorRelease(black);
+ CGContextFillRect(context, inner_rc);
+}
+#else
+void DrawNativeRect(PlatformCanvas& canvas, int x, int y, int w, int h) {
+ notImplemented();
+}
+#endif
+
+// Clips the contents of the canvas to the given rectangle. This will be
+// intersected with any existing clip.
+void AddClip(PlatformCanvas& canvas, int x, int y, int w, int h) {
+ SkRect rect;
+ rect.set(SkIntToScalar(x), SkIntToScalar(y),
+ SkIntToScalar(x + w), SkIntToScalar(y + h));
+ canvas.clipRect(rect);
+}
+
+class LayerSaver {
+ public:
+ LayerSaver(PlatformCanvas& canvas, int x, int y, int w, int h)
+ : canvas_(canvas),
+ x_(x),
+ y_(y),
+ w_(w),
+ h_(h) {
+ SkRect bounds;
+ bounds.set(SkIntToScalar(x_), SkIntToScalar(y_),
+ SkIntToScalar(right()), SkIntToScalar(bottom()));
+ canvas_.saveLayer(&bounds, NULL);
+ canvas.clear(SkColorSetARGB(0, 0, 0, 0));
+ }
+
+ ~LayerSaver() {
+ canvas_.restore();
+ }
+
+ int x() const { return x_; }
+ int y() const { return y_; }
+ int w() const { return w_; }
+ int h() const { return h_; }
+
+ // Returns the EXCLUSIVE far bounds of the layer.
+ int right() const { return x_ + w_; }
+ int bottom() const { return y_ + h_; }
+
+ private:
+ PlatformCanvas& canvas_;
+ int x_, y_, w_, h_;
+};
+
+// Size used for making layers in many of the below tests.
+const int kLayerX = 2;
+const int kLayerY = 3;
+const int kLayerW = 9;
+const int kLayerH = 7;
+
+// Size used by some tests to draw a rectangle inside the layer.
+const int kInnerX = 4;
+const int kInnerY = 5;
+const int kInnerW = 2;
+const int kInnerH = 3;
+
+// Radius used by some tests to draw a rounded-corner rectangle.
+const SkScalar kRadius = 2.0;
+
+}
+
+// This just checks that our checking code is working properly, it just uses
+// regular skia primitives.
+TEST(PlatformCanvas, SkLayer) {
+ // Create the canvas initialized to opaque white.
+ RefPtr<SkCanvas> canvas = AdoptRef(CreatePlatformCanvas(16, 16, true));
+ canvas->drawColor(SK_ColorWHITE);
+
+ // Make a layer and fill it completely to make sure that the bounds are
+ // correct.
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas->drawColor(SK_ColorBLACK);
+ }
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kLayerX, kLayerY, kLayerW, kLayerH));
+}
+
+#if !defined(USE_AURA) // http://crbug.com/154358
+
+// Test native clipping.
+TEST(PlatformCanvas, ClipRegion) {
+ // Initialize a white canvas
+ RefPtr<SkCanvas> canvas = AdoptRef(CreatePlatformCanvas(16, 16, true));
+ canvas->drawColor(SK_ColorWHITE);
+ EXPECT_TRUE(VerifyCanvasColor(*canvas, SK_ColorWHITE));
+
+ // Test that initially the canvas has no clip region, by filling it
+ // with a black rectangle.
+ // Note: Don't use LayerSaver, since internally it sets a clip region.
+ DrawNativeRect(*canvas, 0, 0, 16, 16);
+ EXPECT_TRUE(VerifyCanvasColor(*canvas, SK_ColorBLACK));
+
+ // Test that intersecting disjoint clip rectangles sets an empty clip region
+ canvas->drawColor(SK_ColorWHITE);
+ EXPECT_TRUE(VerifyCanvasColor(*canvas, SK_ColorWHITE));
+ {
+ LayerSaver layer(*canvas, 0, 0, 16, 16);
+ AddClip(*canvas, 2, 3, 4, 5);
+ AddClip(*canvas, 4, 9, 10, 10);
+ DrawNativeRect(*canvas, 0, 0, 16, 16);
+ }
+ EXPECT_TRUE(VerifyCanvasColor(*canvas, SK_ColorWHITE));
+}
+
+#endif // !defined(USE_AURA)
+
+// Test the layers get filled properly by native rendering.
+TEST(PlatformCanvas, FillLayer) {
+ // Create the canvas initialized to opaque white.
+ RefPtr<SkCanvas> canvas = AdoptRef(CreatePlatformCanvas(16, 16, true));
+
+ // Make a layer and fill it completely to make sure that the bounds are
+ // correct.
+ canvas->drawColor(SK_ColorWHITE);
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawNativeRect(*canvas, 0, 0, 100, 100);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), 0, 0, 100, 100);
+#endif
+ }
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kLayerX, kLayerY, kLayerW, kLayerH));
+
+ // Make a layer and fill it partially to make sure the translation is correct.
+ canvas->drawColor(SK_ColorWHITE);
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawNativeRect(*canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), kInnerX, kInnerY, kInnerW, kInnerH);
+#endif
+ }
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+
+ // Add a clip on the layer and fill to make sure clip is correct.
+ canvas->drawColor(SK_ColorWHITE);
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas->save();
+ AddClip(*canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+ DrawNativeRect(*canvas, 0, 0, 100, 100);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), kInnerX, kInnerY, kInnerW, kInnerH);
+#endif
+ canvas->restore();
+ }
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+
+ // Add a clip and then make the layer to make sure the clip is correct.
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->save();
+ AddClip(*canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawNativeRect(*canvas, 0, 0, 100, 100);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), 0, 0, 100, 100);
+#endif
+ }
+ canvas->restore();
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kInnerX, kInnerY, kInnerW, kInnerH));
+}
+
+#if !defined(USE_AURA) // http://crbug.com/154358
+
+// Test that translation + make layer works properly.
+TEST(PlatformCanvas, TranslateLayer) {
+ // Create the canvas initialized to opaque white.
+ RefPtr<SkCanvas> canvas = AdoptRef(CreatePlatformCanvas(16, 16, true));
+
+ // Make a layer and fill it completely to make sure that the bounds are
+ // correct.
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->save();
+ canvas->translate(1, 1);
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawNativeRect(*canvas, 0, 0, 100, 100);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), 0, 0, 100, 100);
+#endif
+ }
+ canvas->restore();
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kLayerX + 1, kLayerY + 1,
+ kLayerW, kLayerH));
+
+ // Translate then make the layer.
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->save();
+ canvas->translate(1, 1);
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ DrawNativeRect(*canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), kInnerX, kInnerY, kInnerW, kInnerH);
+#endif
+ }
+ canvas->restore();
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kInnerX + 1, kInnerY + 1,
+ kInnerW, kInnerH));
+
+ // Make the layer then translate.
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->save();
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas->translate(1, 1);
+ DrawNativeRect(*canvas, kInnerX, kInnerY, kInnerW, kInnerH);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), kInnerX, kInnerY, kInnerW, kInnerH);
+#endif
+ }
+ canvas->restore();
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kInnerX + 1, kInnerY + 1,
+ kInnerW, kInnerH));
+
+ // Translate both before and after, and have a clip.
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->save();
+ canvas->translate(1, 1);
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->translate(1, 1);
+ AddClip(*canvas, kInnerX + 1, kInnerY + 1, kInnerW - 1, kInnerH - 1);
+ DrawNativeRect(*canvas, 0, 0, 100, 100);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), kLayerX, kLayerY, kLayerW, kLayerH);
+#endif
+ }
+ canvas->restore();
+ EXPECT_TRUE(VerifyBlackRect(*canvas, kInnerX + 3, kInnerY + 3,
+ kInnerW - 1, kInnerH - 1));
+
+// TODO(dglazkov): Figure out why this fails on Mac (antialiased clipping?),
+// modify test and remove this guard.
+#if !defined(OS_MACOSX)
+ // Translate both before and after, and have a path clip.
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->save();
+ canvas->translate(1, 1);
+ {
+ LayerSaver layer(*canvas, kLayerX, kLayerY, kLayerW, kLayerH);
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->translate(1, 1);
+
+ SkPath path;
+ SkRect rect;
+ rect.iset(kInnerX - 1, kInnerY - 1,
+ kInnerX + kInnerW, kInnerY + kInnerH);
+ path.addRoundRect(rect, kRadius, kRadius);
+ canvas->clipPath(path);
+
+ DrawNativeRect(*canvas, 0, 0, 100, 100);
+#if defined(OS_WIN)
+ MakeOpaque(canvas.get(), kLayerX, kLayerY, kLayerW, kLayerH);
+#endif
+ }
+ canvas->restore();
+ EXPECT_TRUE(VerifyRoundedRect(*canvas, SK_ColorWHITE, SK_ColorBLACK,
+ kInnerX + 1, kInnerY + 1, kInnerW, kInnerH));
+#endif
+}
+
+#endif // #if !defined(USE_AURA)
+
+TEST(PlatformBitmapTest, PlatformBitmap) {
+ const int kWidth = 400;
+ const int kHeight = 300;
+ scoped_ptr<PlatformBitmap> platform_bitmap(new PlatformBitmap);
+
+ EXPECT_TRUE(0 == platform_bitmap->GetSurface());
+ EXPECT_TRUE(platform_bitmap->GetBitmap().empty());
+ EXPECT_TRUE(platform_bitmap->GetBitmap().isNull());
+
+ EXPECT_TRUE(platform_bitmap->Allocate(kWidth, kHeight, /*is_opaque=*/false));
+
+ EXPECT_TRUE(0 != platform_bitmap->GetSurface());
+ EXPECT_FALSE(platform_bitmap->GetBitmap().empty());
+ EXPECT_FALSE(platform_bitmap->GetBitmap().isNull());
+ EXPECT_EQ(kWidth, platform_bitmap->GetBitmap().width());
+ EXPECT_EQ(kHeight, platform_bitmap->GetBitmap().height());
+ EXPECT_LE(static_cast<size_t>(platform_bitmap->GetBitmap().width()*4),
+ platform_bitmap->GetBitmap().rowBytes());
+ EXPECT_EQ(SkBitmap::kARGB_8888_Config, // Same for all platforms.
+ platform_bitmap->GetBitmap().config());
+ EXPECT_TRUE(platform_bitmap->GetBitmap().lockPixelsAreWritable());
+ EXPECT_TRUE(platform_bitmap->GetBitmap().pixelRef()->isLocked());
+ EXPECT_EQ(1, platform_bitmap->GetBitmap().pixelRef()->getRefCnt());
+
+ *(platform_bitmap->GetBitmap().getAddr32(10, 20)) = 0xDEED1020;
+ *(platform_bitmap->GetBitmap().getAddr32(20, 30)) = 0xDEED2030;
+
+ SkBitmap sk_bitmap = platform_bitmap->GetBitmap();
+ sk_bitmap.lockPixels();
+
+ EXPECT_EQ(2, platform_bitmap->GetBitmap().pixelRef()->getRefCnt());
+ EXPECT_EQ(2, sk_bitmap.pixelRef()->getRefCnt());
+
+ EXPECT_EQ(0xDEED1020, *sk_bitmap.getAddr32(10, 20));
+ EXPECT_EQ(0xDEED2030, *sk_bitmap.getAddr32(20, 30));
+
+ *(platform_bitmap->GetBitmap().getAddr32(30, 40)) = 0xDEED3040;
+
+ // The SkBitmaps derived from a PlatformBitmap must be capable of outliving
+ // the PlatformBitmap.
+ platform_bitmap.reset();
+
+ EXPECT_EQ(1, sk_bitmap.pixelRef()->getRefCnt());
+
+ EXPECT_EQ(0xDEED1020, *sk_bitmap.getAddr32(10, 20));
+ EXPECT_EQ(0xDEED2030, *sk_bitmap.getAddr32(20, 30));
+ EXPECT_EQ(0xDEED3040, *sk_bitmap.getAddr32(30, 40));
+ sk_bitmap.unlockPixels();
+
+ EXPECT_EQ(NULL, sk_bitmap.getPixels());
+
+ sk_bitmap.lockPixels();
+ EXPECT_EQ(0xDEED1020, *sk_bitmap.getAddr32(10, 20));
+ EXPECT_EQ(0xDEED2030, *sk_bitmap.getAddr32(20, 30));
+ EXPECT_EQ(0xDEED3040, *sk_bitmap.getAddr32(30, 40));
+ sk_bitmap.unlockPixels();
+}
+
+
+} // namespace skia
diff --git a/chromium/skia/ext/platform_device.cc b/chromium/skia/ext/platform_device.cc
new file mode 100644
index 00000000000..c7f156516f9
--- /dev/null
+++ b/chromium/skia/ext/platform_device.cc
@@ -0,0 +1,80 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/logging.h"
+#include "skia/ext/platform_device.h"
+
+#include "third_party/skia/include/core/SkMetaData.h"
+
+namespace skia {
+
+namespace {
+
+const char* kDevicePlatformBehaviour = "CrDevicePlatformBehaviour";
+const char* kDraftModeKey = "CrDraftMode";
+
+#if defined(OS_MACOSX) || defined(OS_WIN)
+const char* kIsPreviewMetafileKey = "CrIsPreviewMetafile";
+#endif
+
+void SetBoolMetaData(const SkCanvas& canvas, const char* key, bool value) {
+ SkMetaData& meta = skia::getMetaData(canvas);
+ meta.setBool(key, value);
+}
+
+bool GetBoolMetaData(const SkCanvas& canvas, const char* key) {
+ bool value;
+ SkMetaData& meta = skia::getMetaData(canvas);
+ if (!meta.findBool(key, &value))
+ value = false;
+ return value;
+}
+
+} // namespace
+
+void SetPlatformDevice(SkDevice* device, PlatformDevice* platform_behaviour) {
+ SkMetaData& meta_data = device->getMetaData();
+ meta_data.setPtr(kDevicePlatformBehaviour, platform_behaviour);
+}
+
+PlatformDevice* GetPlatformDevice(SkDevice* device) {
+ if (device) {
+ SkMetaData& meta_data = device->getMetaData();
+ PlatformDevice* device_behaviour = NULL;
+ if (meta_data.findPtr(kDevicePlatformBehaviour,
+ reinterpret_cast<void**>(&device_behaviour)))
+ return device_behaviour;
+ }
+ return NULL;
+}
+
+SkMetaData& getMetaData(const SkCanvas& canvas) {
+ SkDevice* device = canvas.getDevice();
+ DCHECK(device != NULL);
+ return device->getMetaData();
+}
+
+void SetIsDraftMode(const SkCanvas& canvas, bool draft_mode) {
+ SetBoolMetaData(canvas, kDraftModeKey, draft_mode);
+}
+
+bool IsDraftMode(const SkCanvas& canvas) {
+ return GetBoolMetaData(canvas, kDraftModeKey);
+}
+
+#if defined(OS_MACOSX) || defined(OS_WIN)
+void SetIsPreviewMetafile(const SkCanvas& canvas, bool is_preview) {
+ SetBoolMetaData(canvas, kIsPreviewMetafileKey, is_preview);
+}
+
+bool IsPreviewMetafile(const SkCanvas& canvas) {
+ return GetBoolMetaData(canvas, kIsPreviewMetafileKey);
+}
+#endif
+
+bool PlatformDevice::SupportsPlatformPaint() {
+ return true;
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/platform_device.h b/chromium/skia/ext/platform_device.h
new file mode 100644
index 00000000000..4ac3aee5657
--- /dev/null
+++ b/chromium/skia/ext/platform_device.h
@@ -0,0 +1,173 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_PLATFORM_DEVICE_H_
+#define SKIA_EXT_PLATFORM_DEVICE_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include <windows.h>
+#include <vector>
+#endif
+
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkPreConfig.h"
+
+class SkMatrix;
+class SkMetaData;
+class SkPath;
+class SkRegion;
+
+#if defined(OS_LINUX) || defined(OS_OPENBSD) || defined(OS_FREEBSD) \
+ || defined(OS_SOLARIS)
+typedef struct _cairo cairo_t;
+typedef struct _cairo_rectangle cairo_rectangle_t;
+#elif defined(OS_MACOSX)
+typedef struct CGContext* CGContextRef;
+typedef struct CGRect CGRect;
+#endif
+
+namespace skia {
+
+class PlatformDevice;
+
+#if defined(OS_WIN)
+typedef HDC PlatformSurface;
+typedef RECT PlatformRect;
+#elif defined(ANDROID)
+typedef void* PlatformSurface;
+typedef SkIRect* PlatformRect;
+#elif defined(OS_LINUX) || defined(OS_OPENBSD) || defined(OS_FREEBSD) \
+ || defined(OS_SOLARIS)
+typedef cairo_t* PlatformSurface;
+typedef cairo_rectangle_t PlatformRect;
+#elif defined(OS_MACOSX)
+typedef CGContextRef PlatformSurface;
+typedef CGRect PlatformRect;
+#endif
+
+// The following routines provide accessor points for the functionality
+// exported by the various PlatformDevice ports. The PlatformDevice, and
+// BitmapPlatformDevice classes inherit directly from SkDevice, which is no
+// longer a supported usage-pattern for skia. In preparation of the removal of
+// these classes, all calls to PlatformDevice::* should be routed through these
+// helper functions.
+
+// Bind a PlatformDevice instance, |platform_device| to |device|. Subsequent
+// calls to the functions exported below will forward the request to the
+// corresponding method on the bound PlatformDevice instance. If no
+// PlatformDevice has been bound to the SkDevice passed, then the routines are
+// NOPS.
+SK_API void SetPlatformDevice(SkDevice* device,
+ PlatformDevice* platform_device);
+SK_API PlatformDevice* GetPlatformDevice(SkDevice* device);
+
+
+#if defined(OS_WIN)
+// Initializes the default settings and colors in a device context.
+SK_API void InitializeDC(HDC context);
+#elif defined(OS_MACOSX)
+// Returns the CGContext that backing the SkDevice. Forwards to the bound
+// PlatformDevice. Returns NULL if no PlatformDevice is bound.
+SK_API CGContextRef GetBitmapContext(SkDevice* device);
+#endif
+
+// Following routines are used in print preview workflow to mark the draft mode
+// metafile and preview metafile.
+SK_API SkMetaData& getMetaData(const SkCanvas& canvas);
+SK_API void SetIsDraftMode(const SkCanvas& canvas, bool draft_mode);
+SK_API bool IsDraftMode(const SkCanvas& canvas);
+
+#if defined(OS_MACOSX) || defined(OS_WIN)
+SK_API void SetIsPreviewMetafile(const SkCanvas& canvas, bool is_preview);
+SK_API bool IsPreviewMetafile(const SkCanvas& canvas);
+#endif
+
+// A SkDevice is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. PlatformDevice provides a surface Windows can also
+// write to. It also provides functionality to play well with GDI drawing
+// functions. This class is abstract and must be subclassed. It provides the
+// basic interface to implement it either with or without a bitmap backend.
+//
+// PlatformDevice provides an interface which sub-classes of SkDevice can also
+// provide to allow for drawing by the native platform into the device.
+class SK_API PlatformDevice {
+ public:
+ virtual ~PlatformDevice() {}
+
+#if defined(OS_MACOSX)
+ // The CGContext that corresponds to the bitmap, used for CoreGraphics
+ // operations drawing into the bitmap. This is possibly heavyweight, so it
+ // should exist only during one pass of rendering.
+ virtual CGContextRef GetBitmapContext() = 0;
+#endif
+
+ // The DC that corresponds to the bitmap, used for GDI operations drawing
+ // into the bitmap. This is possibly heavyweight, so it should be existant
+ // only during one pass of rendering.
+ virtual PlatformSurface BeginPlatformPaint();
+
+ // Finish a previous call to beginPlatformPaint.
+ virtual void EndPlatformPaint();
+
+ // Draws to the given screen DC, if the bitmap DC doesn't exist, this will
+ // temporarily create it. However, if you have created the bitmap DC, it will
+ // be more efficient if you don't free it until after this call so it doesn't
+ // have to be created twice. If src_rect is null, then the entirety of the
+ // source device will be copied.
+ virtual void DrawToNativeContext(PlatformSurface surface, int x, int y,
+ const PlatformRect* src_rect) = 0;
+
+ // Returns true if GDI operations can be used for drawing into the bitmap.
+ virtual bool SupportsPlatformPaint();
+
+#if defined(OS_WIN)
+ // Loads a SkPath into the GDI context. The path can there after be used for
+ // clipping or as a stroke. Returns false if the path failed to be loaded.
+ static bool LoadPathToDC(HDC context, const SkPath& path);
+
+ // Loads a SkRegion into the GDI context.
+ static void LoadClippingRegionToDC(HDC context, const SkRegion& region,
+ const SkMatrix& transformation);
+#elif defined(OS_MACOSX)
+ // Loads a SkPath into the CG context. The path can there after be used for
+ // clipping or as a stroke.
+ static void LoadPathToCGContext(CGContextRef context, const SkPath& path);
+
+ // Initializes the default settings and colors in a device context.
+ static void InitializeCGContext(CGContextRef context);
+
+ // Loads a SkRegion into the CG context.
+ static void LoadClippingRegionToCGContext(CGContextRef context,
+ const SkRegion& region,
+ const SkMatrix& transformation);
+#endif
+
+ protected:
+#if defined(OS_WIN)
+ // Arrays must be inside structures.
+ struct CubicPoints {
+ SkPoint p[4];
+ };
+ typedef std::vector<CubicPoints> CubicPath;
+ typedef std::vector<CubicPath> CubicPaths;
+
+ // Loads the specified Skia transform into the device context, excluding
+ // perspective (which GDI doesn't support).
+ static void LoadTransformToDC(HDC dc, const SkMatrix& matrix);
+
+ // Transforms SkPath's paths into a series of cubic path.
+ static bool SkPathToCubicPaths(CubicPaths* paths, const SkPath& skpath);
+#elif defined(OS_MACOSX)
+ // Loads the specified Skia transform into the device context
+ static void LoadTransformToCGContext(CGContextRef context,
+ const SkMatrix& matrix);
+#endif
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_PLATFORM_DEVICE_H_
diff --git a/chromium/skia/ext/platform_device_linux.cc b/chromium/skia/ext/platform_device_linux.cc
new file mode 100644
index 00000000000..f72e6148c4e
--- /dev/null
+++ b/chromium/skia/ext/platform_device_linux.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/platform_device.h"
+
+namespace skia {
+
+PlatformSurface PlatformDevice::BeginPlatformPaint() {
+ return NULL;
+}
+
+void PlatformDevice::EndPlatformPaint() {
+ // We don't need to do anything on Linux here.
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/platform_device_mac.cc b/chromium/skia/ext/platform_device_mac.cc
new file mode 100644
index 00000000000..6ff017d5cd7
--- /dev/null
+++ b/chromium/skia/ext/platform_device_mac.cc
@@ -0,0 +1,155 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/platform_device.h"
+#include "skia/ext/bitmap_platform_device.h"
+
+#import <ApplicationServices/ApplicationServices.h>
+#include "skia/ext/skia_utils_mac.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkTypes.h"
+#include "third_party/skia/include/core/SkUtils.h"
+
+namespace skia {
+
+CGContextRef GetBitmapContext(SkDevice* device) {
+ PlatformDevice* platform_device = GetPlatformDevice(device);
+ if (platform_device)
+ return platform_device->GetBitmapContext();
+
+ return NULL;
+}
+
+CGContextRef PlatformDevice::BeginPlatformPaint() {
+ return GetBitmapContext();
+}
+
+void PlatformDevice::EndPlatformPaint() {
+ // Flushing will be done in onAccessBitmap.
+}
+
+// Set up the CGContextRef for peaceful coexistence with Skia
+void PlatformDevice::InitializeCGContext(CGContextRef context) {
+ // CG defaults to the same settings as Skia
+}
+
+// static
+void PlatformDevice::LoadPathToCGContext(CGContextRef context,
+ const SkPath& path) {
+ // instead of a persistent attribute of the context, CG specifies the fill
+ // type per call, so we just have to load up the geometry.
+ CGContextBeginPath(context);
+
+ SkPoint points[4] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+ SkPath::Iter iter(path, false);
+ for (SkPath::Verb verb = iter.next(points); verb != SkPath::kDone_Verb;
+ verb = iter.next(points)) {
+ switch (verb) {
+ case SkPath::kMove_Verb: { // iter.next returns 1 point
+ CGContextMoveToPoint(context, points[0].fX, points[0].fY);
+ break;
+ }
+ case SkPath::kLine_Verb: { // iter.next returns 2 points
+ CGContextAddLineToPoint(context, points[1].fX, points[1].fY);
+ break;
+ }
+ case SkPath::kQuad_Verb: { // iter.next returns 3 points
+ CGContextAddQuadCurveToPoint(context, points[1].fX, points[1].fY,
+ points[2].fX, points[2].fY);
+ break;
+ }
+ case SkPath::kCubic_Verb: { // iter.next returns 4 points
+ CGContextAddCurveToPoint(context, points[1].fX, points[1].fY,
+ points[2].fX, points[2].fY,
+ points[3].fX, points[3].fY);
+ break;
+ }
+ case SkPath::kClose_Verb: { // iter.next returns 1 point (the last point)
+ break;
+ }
+ case SkPath::kDone_Verb: // iter.next returns 0 points
+ default: {
+ SkASSERT(false);
+ break;
+ }
+ }
+ }
+ CGContextClosePath(context);
+}
+
+// static
+void PlatformDevice::LoadTransformToCGContext(CGContextRef context,
+ const SkMatrix& matrix) {
+ // CoreGraphics can concatenate transforms, but not reset the current one.
+ // So in order to get the required behavior here, we need to first make
+ // the current transformation matrix identity and only then load the new one.
+
+ // Reset matrix to identity.
+ CGAffineTransform orig_cg_matrix = CGContextGetCTM(context);
+ CGAffineTransform orig_cg_matrix_inv = CGAffineTransformInvert(
+ orig_cg_matrix);
+ CGContextConcatCTM(context, orig_cg_matrix_inv);
+
+ // assert that we have indeed returned to the identity Matrix.
+ SkASSERT(CGAffineTransformIsIdentity(CGContextGetCTM(context)));
+
+ // Convert xform to CG-land.
+ // Our coordinate system is flipped to match WebKit's so we need to modify
+ // the xform to match that.
+ SkMatrix transformed_matrix = matrix;
+ SkScalar sy = matrix.getScaleY() * (SkScalar)-1;
+ transformed_matrix.setScaleY(sy);
+ size_t height = CGBitmapContextGetHeight(context);
+ SkScalar ty = -matrix.getTranslateY(); // y axis is flipped.
+ transformed_matrix.setTranslateY(ty + (SkScalar)height);
+
+ CGAffineTransform cg_matrix = gfx::SkMatrixToCGAffineTransform(
+ transformed_matrix);
+
+ // Load final transform into context.
+ CGContextConcatCTM(context, cg_matrix);
+}
+
+// static
+void PlatformDevice::LoadClippingRegionToCGContext(
+ CGContextRef context,
+ const SkRegion& region,
+ const SkMatrix& transformation) {
+ if (region.isEmpty()) {
+ // region can be empty, in which case everything will be clipped.
+ SkRect rect;
+ rect.setEmpty();
+ CGContextClipToRect(context, gfx::SkRectToCGRect(rect));
+ } else if (region.isRect()) {
+ // CoreGraphics applies the current transform to clip rects, which is
+ // unwanted. Inverse-transform the rect before sending it to CG. This only
+ // works for translations and scaling, but not for rotations (but the
+ // viewport is never rotated anyway).
+ SkMatrix t;
+ bool did_invert = transformation.invert(&t);
+ if (!did_invert)
+ t.reset();
+ // Do the transformation.
+ SkRect rect;
+ rect.set(region.getBounds());
+ t.mapRect(&rect);
+ SkIRect irect;
+ rect.round(&irect);
+ CGContextClipToRect(context, gfx::SkIRectToCGRect(irect));
+ } else {
+ // It is complex.
+ SkPath path;
+ region.getBoundaryPath(&path);
+ // Clip. Note that windows clipping regions are not affected by the
+ // transform so apply it manually.
+ path.transform(transformation);
+ // TODO(playmobil): Implement.
+ SkASSERT(false);
+ // LoadPathToDC(context, path);
+ // hrgn = PathToRegion(context);
+ }
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/platform_device_win.cc b/chromium/skia/ext/platform_device_win.cc
new file mode 100644
index 00000000000..f44e66eb43d
--- /dev/null
+++ b/chromium/skia/ext/platform_device_win.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/platform_device.h"
+
+#include "skia/ext/skia_utils_win.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkPath.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/core/SkUtils.h"
+
+namespace skia {
+
+void InitializeDC(HDC context) {
+ // Enables world transformation.
+ // If the GM_ADVANCED graphics mode is set, GDI always draws arcs in the
+ // counterclockwise direction in logical space. This is equivalent to the
+ // statement that, in the GM_ADVANCED graphics mode, both arc control points
+ // and arcs themselves fully respect the device context's world-to-device
+ // transformation.
+ BOOL res = SetGraphicsMode(context, GM_ADVANCED);
+ SkASSERT(res != 0);
+
+ // Enables dithering.
+ res = SetStretchBltMode(context, HALFTONE);
+ SkASSERT(res != 0);
+ // As per SetStretchBltMode() documentation, SetBrushOrgEx() must be called
+ // right after.
+ res = SetBrushOrgEx(context, 0, 0, NULL);
+ SkASSERT(res != 0);
+
+ // Sets up default orientation.
+ res = SetArcDirection(context, AD_CLOCKWISE);
+ SkASSERT(res != 0);
+
+ // Sets up default colors.
+ res = SetBkColor(context, RGB(255, 255, 255));
+ SkASSERT(res != CLR_INVALID);
+ res = SetTextColor(context, RGB(0, 0, 0));
+ SkASSERT(res != CLR_INVALID);
+ res = SetDCBrushColor(context, RGB(255, 255, 255));
+ SkASSERT(res != CLR_INVALID);
+ res = SetDCPenColor(context, RGB(0, 0, 0));
+ SkASSERT(res != CLR_INVALID);
+
+ // Sets up default transparency.
+ res = SetBkMode(context, OPAQUE);
+ SkASSERT(res != 0);
+ res = SetROP2(context, R2_COPYPEN);
+ SkASSERT(res != 0);
+}
+
+PlatformSurface PlatformDevice::BeginPlatformPaint() {
+ return 0;
+}
+
+void PlatformDevice::EndPlatformPaint() {
+ // We don't clear the DC here since it will be likely to be used again.
+ // Flushing will be done in onAccessBitmap.
+}
+
+void PlatformDevice::DrawToNativeContext(PlatformSurface surface, int x, int y,
+ const PlatformRect* src_rect) {
+}
+
+// static
+bool PlatformDevice::LoadPathToDC(HDC context, const SkPath& path) {
+ switch (path.getFillType()) {
+ case SkPath::kWinding_FillType: {
+ int res = SetPolyFillMode(context, WINDING);
+ SkASSERT(res != 0);
+ break;
+ }
+ case SkPath::kEvenOdd_FillType: {
+ int res = SetPolyFillMode(context, ALTERNATE);
+ SkASSERT(res != 0);
+ break;
+ }
+ default: {
+ SkASSERT(false);
+ break;
+ }
+ }
+ BOOL res = BeginPath(context);
+ if (!res) {
+ return false;
+ }
+
+ CubicPaths paths;
+ if (!SkPathToCubicPaths(&paths, path))
+ return false;
+
+ std::vector<POINT> points;
+ for (CubicPaths::const_iterator path(paths.begin()); path != paths.end();
+ ++path) {
+ if (!path->size())
+ continue;
+ points.resize(0);
+ points.reserve(path->size() * 3 / 4 + 1);
+ points.push_back(SkPointToPOINT(path->front().p[0]));
+ for (CubicPath::const_iterator point(path->begin()); point != path->end();
+ ++point) {
+ // Never add point->p[0]
+ points.push_back(SkPointToPOINT(point->p[1]));
+ points.push_back(SkPointToPOINT(point->p[2]));
+ points.push_back(SkPointToPOINT(point->p[3]));
+ }
+ SkASSERT((points.size() - 1) % 3 == 0);
+ // This is slightly inefficient since all straight line and quadratic lines
+ // are "upgraded" to a cubic line.
+ // TODO(maruel): http://b/1147346 We should use
+ // PolyDraw/PolyBezier/Polyline whenever possible.
+ res = PolyBezier(context, &points.front(),
+ static_cast<DWORD>(points.size()));
+ SkASSERT(res != 0);
+ if (res == 0)
+ break;
+ }
+ if (res == 0) {
+ // Make sure the path is discarded.
+ AbortPath(context);
+ } else {
+ res = EndPath(context);
+ SkASSERT(res != 0);
+ }
+ return true;
+}
+
+// static
+void PlatformDevice::LoadTransformToDC(HDC dc, const SkMatrix& matrix) {
+ XFORM xf;
+ xf.eM11 = matrix[SkMatrix::kMScaleX];
+ xf.eM21 = matrix[SkMatrix::kMSkewX];
+ xf.eDx = matrix[SkMatrix::kMTransX];
+ xf.eM12 = matrix[SkMatrix::kMSkewY];
+ xf.eM22 = matrix[SkMatrix::kMScaleY];
+ xf.eDy = matrix[SkMatrix::kMTransY];
+ SetWorldTransform(dc, &xf);
+}
+
+// static
+bool PlatformDevice::SkPathToCubicPaths(CubicPaths* paths,
+ const SkPath& skpath) {
+ paths->clear();
+ CubicPath* current_path = NULL;
+ SkPoint current_points[4];
+ CubicPoints points_to_add;
+ SkPath::Iter iter(skpath, false);
+ for (SkPath::Verb verb = iter.next(current_points);
+ verb != SkPath::kDone_Verb;
+ verb = iter.next(current_points)) {
+ switch (verb) {
+ case SkPath::kMove_Verb: { // iter.next returns 1 point
+ // Ignores it since the point is copied in the next operation. See
+ // SkPath::Iter::next() for reference.
+ paths->push_back(CubicPath());
+ current_path = &paths->back();
+ // Skip point addition.
+ continue;
+ }
+ case SkPath::kLine_Verb: { // iter.next returns 2 points
+ points_to_add.p[0] = current_points[0];
+ points_to_add.p[1] = current_points[0];
+ points_to_add.p[2] = current_points[1];
+ points_to_add.p[3] = current_points[1];
+ break;
+ }
+ case SkPath::kQuad_Verb: { // iter.next returns 3 points
+ points_to_add.p[0] = current_points[0];
+ points_to_add.p[1] = current_points[1];
+ points_to_add.p[2] = current_points[2];
+ points_to_add.p[3] = current_points[2];
+ break;
+ }
+ case SkPath::kCubic_Verb: { // iter.next returns 4 points
+ points_to_add.p[0] = current_points[0];
+ points_to_add.p[1] = current_points[1];
+ points_to_add.p[2] = current_points[2];
+ points_to_add.p[3] = current_points[3];
+ break;
+ }
+ case SkPath::kClose_Verb: { // iter.next returns 1 point (the last point)
+ paths->push_back(CubicPath());
+ current_path = &paths->back();
+ continue;
+ }
+ default: {
+ current_path = NULL;
+ // Will return false.
+ break;
+ }
+ }
+ SkASSERT(current_path);
+ if (!current_path) {
+ paths->clear();
+ return false;
+ }
+ current_path->push_back(points_to_add);
+ }
+ return true;
+}
+
+// static
+void PlatformDevice::LoadClippingRegionToDC(HDC context,
+ const SkRegion& region,
+ const SkMatrix& transformation) {
+ HRGN hrgn;
+ if (region.isEmpty()) {
+ // region can be empty, in which case everything will be clipped.
+ hrgn = CreateRectRgn(0, 0, 0, 0);
+ } else if (region.isRect()) {
+ // We don't apply transformation, because the translation is already applied
+ // to the region.
+ hrgn = CreateRectRgnIndirect(&SkIRectToRECT(region.getBounds()));
+ } else {
+ // It is complex.
+ SkPath path;
+ region.getBoundaryPath(&path);
+ // Clip. Note that windows clipping regions are not affected by the
+ // transform so apply it manually.
+ // Since the transform is given as the original translation of canvas, we
+ // should apply it in reverse.
+ SkMatrix t(transformation);
+ t.setTranslateX(-t.getTranslateX());
+ t.setTranslateY(-t.getTranslateY());
+ path.transform(t);
+ LoadPathToDC(context, path);
+ hrgn = PathToRegion(context);
+ }
+ int result = SelectClipRgn(context, hrgn);
+ SkASSERT(result != ERROR);
+ result = DeleteObject(hrgn);
+ SkASSERT(result != 0);
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/recursive_gaussian_convolution.cc b/chromium/skia/ext/recursive_gaussian_convolution.cc
new file mode 100644
index 00000000000..195fca8deae
--- /dev/null
+++ b/chromium/skia/ext/recursive_gaussian_convolution.cc
@@ -0,0 +1,270 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <algorithm>
+#include <cmath>
+#include <vector>
+
+#include "base/logging.h"
+#include "skia/ext/recursive_gaussian_convolution.h"
+
+namespace skia {
+
+namespace {
+
+// Takes the value produced by accumulating element-wise product of image with
+// a kernel and brings it back into range.
+// All of the filter scaling factors are in fixed point with kShiftBits bits of
+// fractional part.
+template<bool take_absolute>
+inline unsigned char FloatTo8(float f) {
+ int a = static_cast<int>(f + 0.5f);
+ if (take_absolute)
+ a = std::abs(a);
+ else if (a < 0)
+ return 0;
+ if (a < 256)
+ return a;
+ return 255;
+}
+
+template<RecursiveFilter::Order order>
+inline float ForwardFilter(float in_n_1,
+ float in_n,
+ float in_n1,
+ const std::vector<float>& w,
+ int n,
+ const float* b) {
+ switch (order) {
+ case RecursiveFilter::FUNCTION:
+ return b[0] * in_n + b[1] * w[n-1] + b[2] * w[n-2] + b[3] * w[n-3];
+ case RecursiveFilter::FIRST_DERIVATIVE:
+ return b[0] * 0.5f * (in_n1 - in_n_1) +
+ b[1] * w[n-1] + b[2] * w[n-2] + b[3] * w[n-3];
+ case RecursiveFilter::SECOND_DERIVATIVE:
+ return b[0] * (in_n - in_n_1) +
+ b[1] * w[n-1] + b[2] * w[n-2] + b[3] * w[n-3];
+ }
+
+ NOTREACHED();
+ return 0.0f;
+}
+
+template<RecursiveFilter::Order order>
+inline float BackwardFilter(const std::vector<float>& out,
+ int n,
+ float w_n,
+ float w_n1,
+ const float* b) {
+ switch (order) {
+ case RecursiveFilter::FUNCTION:
+ case RecursiveFilter::FIRST_DERIVATIVE:
+ return b[0] * w_n +
+ b[1] * out[n + 1] + b[2] * out[n + 2] + b[3] * out[n + 3];
+ case RecursiveFilter::SECOND_DERIVATIVE:
+ return b[0] * (w_n1 - w_n) +
+ b[1] * out[n + 1] + b[2] * out[n + 2] + b[3] * out[n + 3];
+ }
+ NOTREACHED();
+ return 0.0f;
+}
+
+template<RecursiveFilter::Order order, bool absolute_values>
+unsigned char SingleChannelRecursiveFilter(
+ const unsigned char* const source_data,
+ int source_pixel_stride,
+ int source_row_stride,
+ int row_width,
+ int row_count,
+ unsigned char* const output,
+ int output_pixel_stride,
+ int output_row_stride,
+ const float* b) {
+ const int intermediate_buffer_size = row_width + 6;
+ std::vector<float> w(intermediate_buffer_size);
+ const unsigned char* in = source_data;
+ unsigned char* out = output;
+ unsigned char max_output = 0;
+ for (int r = 0; r < row_count;
+ ++r, in += source_row_stride, out += output_row_stride) {
+ // Compute forward filter.
+ // First initialize start of the w (temporary) vector.
+ if (order == RecursiveFilter::FUNCTION)
+ w[0] = w[1] = w[2] = in[0];
+ else
+ w[0] = w[1] = w[2] = 0.0f;
+ // Note that special-casing of w[3] is needed because of derivatives.
+ w[3] = ForwardFilter<order>(
+ in[0], in[0], in[source_pixel_stride], w, 3, b);
+ int n = 4;
+ int c = 1;
+ int byte_index = source_pixel_stride;
+ for (; c < row_width - 1; ++c, ++n, byte_index += source_pixel_stride) {
+ w[n] = ForwardFilter<order>(in[byte_index - source_pixel_stride],
+ in[byte_index],
+ in[byte_index + source_pixel_stride],
+ w, n, b);
+ }
+
+ // The value of w corresponding to the last image pixel needs to be computed
+ // separately, again because of derivatives.
+ w[n] = ForwardFilter<order>(in[byte_index - source_pixel_stride],
+ in[byte_index],
+ in[byte_index],
+ w, n, b);
+ // Now three trailing bytes set to the same value as current w[n].
+ w[n + 1] = w[n];
+ w[n + 2] = w[n];
+ w[n + 3] = w[n];
+
+ // Now apply the back filter.
+ float w_n1 = w[n + 1];
+ int output_index = (row_width - 1) * output_pixel_stride;
+ for (; c >= 0; output_index -= output_pixel_stride, --c, --n) {
+ float w_n = BackwardFilter<order>(w, n, w[n], w_n1, b);
+ w_n1 = w[n];
+ w[n] = w_n;
+ out[output_index] = FloatTo8<absolute_values>(w_n);
+ max_output = std::max(max_output, out[output_index]);
+ }
+ }
+ return max_output;
+}
+
+unsigned char SingleChannelRecursiveFilter(
+ const unsigned char* const source_data,
+ int source_pixel_stride,
+ int source_row_stride,
+ int row_width,
+ int row_count,
+ unsigned char* const output,
+ int output_pixel_stride,
+ int output_row_stride,
+ const float* b,
+ RecursiveFilter::Order order,
+ bool absolute_values) {
+ if (absolute_values) {
+ switch (order) {
+ case RecursiveFilter::FUNCTION:
+ return SingleChannelRecursiveFilter<RecursiveFilter::FUNCTION, true>(
+ source_data, source_pixel_stride, source_row_stride,
+ row_width, row_count,
+ output, output_pixel_stride, output_row_stride, b);
+ case RecursiveFilter::FIRST_DERIVATIVE:
+ return SingleChannelRecursiveFilter<
+ RecursiveFilter::FIRST_DERIVATIVE, true>(
+ source_data, source_pixel_stride, source_row_stride,
+ row_width, row_count,
+ output, output_pixel_stride, output_row_stride, b);
+ case RecursiveFilter::SECOND_DERIVATIVE:
+ return SingleChannelRecursiveFilter<
+ RecursiveFilter::SECOND_DERIVATIVE, true>(
+ source_data, source_pixel_stride, source_row_stride,
+ row_width, row_count,
+ output, output_pixel_stride, output_row_stride, b);
+ }
+ } else {
+ switch (order) {
+ case RecursiveFilter::FUNCTION:
+ return SingleChannelRecursiveFilter<RecursiveFilter::FUNCTION, false>(
+ source_data, source_pixel_stride, source_row_stride,
+ row_width, row_count,
+ output, output_pixel_stride, output_row_stride, b);
+ case RecursiveFilter::FIRST_DERIVATIVE:
+ return SingleChannelRecursiveFilter<
+ RecursiveFilter::FIRST_DERIVATIVE, false>(
+ source_data, source_pixel_stride, source_row_stride,
+ row_width, row_count,
+ output, output_pixel_stride, output_row_stride, b);
+ case RecursiveFilter::SECOND_DERIVATIVE:
+ return SingleChannelRecursiveFilter<
+ RecursiveFilter::SECOND_DERIVATIVE, false>(
+ source_data, source_pixel_stride, source_row_stride,
+ row_width, row_count,
+ output, output_pixel_stride, output_row_stride, b);
+ }
+ }
+
+ NOTREACHED();
+ return 0;
+}
+
+}
+
+float RecursiveFilter::qFromSigma(float sigma) {
+ DCHECK_GE(sigma, 0.5f);
+ if (sigma <= 2.5f)
+ return 3.97156f - 4.14554f * std::sqrt(1.0f - 0.26891f * sigma);
+ return 0.98711f * sigma - 0.96330f;
+}
+
+void RecursiveFilter::computeCoefficients(float q, float b[4]) {
+ b[0] = 1.57825f + 2.44413f * q + 1.4281f * q * q + 0.422205f * q * q * q;
+ b[1] = 2.4413f * q + 2.85619f * q * q + 1.26661f * q * q * q;
+ b[2] = - 1.4281f * q * q - 1.26661f * q * q * q;
+ b[3] = 0.422205f * q * q * q;
+
+ // The above is exactly like in the paper. To cut down on computations,
+ // we can fix up these numbers a bit now.
+ float b_norm = 1.0f - (b[1] + b[2] + b[3]) / b[0];
+ b[1] /= b[0];
+ b[2] /= b[0];
+ b[3] /= b[0];
+ b[0] = b_norm;
+}
+
+RecursiveFilter::RecursiveFilter(float sigma, Order order)
+ : order_(order), q_(qFromSigma(sigma)) {
+ computeCoefficients(q_, b_);
+}
+
+unsigned char SingleChannelRecursiveGaussianX(const unsigned char* source_data,
+ int source_byte_row_stride,
+ int input_channel_index,
+ int input_channel_count,
+ const RecursiveFilter& filter,
+ const SkISize& image_size,
+ unsigned char* output,
+ int output_byte_row_stride,
+ int output_channel_index,
+ int output_channel_count,
+ bool absolute_values) {
+ return SingleChannelRecursiveFilter(source_data + input_channel_index,
+ input_channel_count,
+ source_byte_row_stride,
+ image_size.width(),
+ image_size.height(),
+ output + output_channel_index,
+ output_channel_count,
+ output_byte_row_stride,
+ filter.b(),
+ filter.order(),
+ absolute_values);
+}
+
+unsigned char SingleChannelRecursiveGaussianY(const unsigned char* source_data,
+ int source_byte_row_stride,
+ int input_channel_index,
+ int input_channel_count,
+ const RecursiveFilter& filter,
+ const SkISize& image_size,
+ unsigned char* output,
+ int output_byte_row_stride,
+ int output_channel_index,
+ int output_channel_count,
+ bool absolute_values) {
+ return SingleChannelRecursiveFilter(source_data + input_channel_index,
+ source_byte_row_stride,
+ input_channel_count,
+ image_size.height(),
+ image_size.width(),
+ output + output_channel_index,
+ output_byte_row_stride,
+ output_channel_count,
+ filter.b(),
+ filter.order(),
+ absolute_values);
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/recursive_gaussian_convolution.h b/chromium/skia/ext/recursive_gaussian_convolution.h
new file mode 100644
index 00000000000..a4843292e1a
--- /dev/null
+++ b/chromium/skia/ext/recursive_gaussian_convolution.h
@@ -0,0 +1,71 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_RECURSIVE_GAUSSIAN_CONVOLUTION_H_
+#define SKIA_EXT_RECURSIVE_GAUSSIAN_CONVOLUTION_H_
+
+#include "skia/ext/convolver.h"
+#include "third_party/skia/include/core/SkSize.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+namespace skia {
+
+// RecursiveFilter, paired with SingleChannelRecursiveGaussianX and
+// SingleChannelRecursiveGaussianY routines below implement recursive filters as
+// described in 'Recursive implementation of the Gaussian filter' (Young, Vliet)
+// (1995). Single-letter variable names mirror exactly the usage in the paper to
+// ease reading and analysis.
+class RecursiveFilter {
+ public:
+ enum Order {
+ FUNCTION,
+ FIRST_DERIVATIVE,
+ SECOND_DERIVATIVE
+ };
+
+ static float qFromSigma(float sigma);
+ static void computeCoefficients(float q, float b[4]);
+ SK_API RecursiveFilter(float sigma, Order order);
+
+ Order order() const { return order_; }
+ const float* b() const { return b_; }
+
+ private:
+ Order order_;
+ float q_;
+ float b_[4];
+};
+
+// Applies a gaussian recursive filter given as |filter| to a single channel at
+// |input_channel_index| to image given in |source_data| along X axis.
+// The output is placed into |output| into channel |output_channel_index|.
+SK_API unsigned char SingleChannelRecursiveGaussianX(
+ const unsigned char* source_data,
+ int source_byte_row_stride,
+ int input_channel_index,
+ int input_channel_count,
+ const RecursiveFilter& filter,
+ const SkISize& image_size,
+ unsigned char* output,
+ int output_byte_row_stride,
+ int output_channel_index,
+ int output_channel_count,
+ bool absolute_values);
+
+// Applies a gaussian recursive filter along Y axis.
+SK_API unsigned char SingleChannelRecursiveGaussianY(
+ const unsigned char* source_data,
+ int source_byte_row_stride,
+ int input_channel_index,
+ int input_channel_count,
+ const RecursiveFilter& filter,
+ const SkISize& image_size,
+ unsigned char* output,
+ int output_byte_row_stride,
+ int output_channel_index,
+ int output_channel_count,
+ bool absolute_values);
+} // namespace skia
+
+#endif // SKIA_EXT_RECURSIVE_GAUSSIAN_CONVOLUTION_H_
diff --git a/chromium/skia/ext/recursive_gaussian_convolution_unittest.cc b/chromium/skia/ext/recursive_gaussian_convolution_unittest.cc
new file mode 100644
index 00000000000..3f00bac68b0
--- /dev/null
+++ b/chromium/skia/ext/recursive_gaussian_convolution_unittest.cc
@@ -0,0 +1,395 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <functional>
+#include <numeric>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "skia/ext/convolver.h"
+#include "skia/ext/recursive_gaussian_convolution.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkPoint.h"
+#include "third_party/skia/include/core/SkRect.h"
+
+namespace {
+
+int ComputeRowStride(int width, int channel_count, int stride_slack) {
+ return width * channel_count + stride_slack;
+}
+
+SkIPoint MakeImpulseImage(std::vector<unsigned char>* image,
+ int width,
+ int height,
+ int channel_index,
+ int channel_count,
+ int stride_slack) {
+ const int src_row_stride = ComputeRowStride(
+ width, channel_count, stride_slack);
+ const int src_byte_count = src_row_stride * height;
+ const int signal_x = width / 2;
+ const int signal_y = height / 2;
+
+ image->resize(src_byte_count, 0);
+ const int non_zero_pixel_index =
+ signal_y * src_row_stride + signal_x * channel_count + channel_index;
+ (*image)[non_zero_pixel_index] = 255;
+ return SkIPoint::Make(signal_x, signal_y);
+}
+
+SkIRect MakeBoxImage(std::vector<unsigned char>* image,
+ int width,
+ int height,
+ int channel_index,
+ int channel_count,
+ int stride_slack,
+ int box_width,
+ int box_height,
+ unsigned char value) {
+ const int src_row_stride = ComputeRowStride(
+ width, channel_count, stride_slack);
+ const int src_byte_count = src_row_stride * height;
+ const SkIRect box = SkIRect::MakeXYWH((width - box_width) / 2,
+ (height - box_height) / 2,
+ box_width, box_height);
+
+ image->resize(src_byte_count, 0);
+ for (int y = box.top(); y < box.bottom(); ++y) {
+ for (int x = box.left(); x < box.right(); ++x)
+ (*image)[y * src_row_stride + x * channel_count + channel_index] = value;
+ }
+
+ return box;
+}
+
+int ComputeBoxSum(const std::vector<unsigned char>& image,
+ const SkIRect& box,
+ int image_width) {
+ // Compute the sum of all pixels in the box. Assume byte stride 1 and row
+ // stride same as image_width.
+ int sum = 0;
+ for (int y = box.top(); y < box.bottom(); ++y) {
+ for (int x = box.left(); x < box.right(); ++x)
+ sum += image[y * image_width + x];
+ }
+
+ return sum;
+}
+
+} // namespace
+
+namespace skia {
+
+TEST(RecursiveGaussian, SmoothingMethodComparison) {
+ static const int kImgWidth = 512;
+ static const int kImgHeight = 220;
+ static const int kChannelIndex = 3;
+ static const int kChannelCount = 3;
+ static const int kStrideSlack = 22;
+
+ std::vector<unsigned char> input;
+ SkISize image_size = SkISize::Make(kImgWidth, kImgHeight);
+ MakeImpulseImage(
+ &input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount,
+ kStrideSlack);
+
+ // Destination will be a single channel image with stide matching width.
+ const int dest_row_stride = kImgWidth;
+ const int dest_byte_count = dest_row_stride * kImgHeight;
+ std::vector<unsigned char> intermediate(dest_byte_count);
+ std::vector<unsigned char> intermediate2(dest_byte_count);
+ std::vector<unsigned char> control(dest_byte_count);
+ std::vector<unsigned char> output(dest_byte_count);
+
+ const int src_row_stride = ComputeRowStride(
+ kImgWidth, kChannelCount, kStrideSlack);
+
+ const float kernel_sigma = 2.5f;
+ ConvolutionFilter1D filter;
+ SetUpGaussianConvolutionKernel(&filter, kernel_sigma, false);
+ // Process the control image.
+ SingleChannelConvolveX1D(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ filter, image_size,
+ &intermediate[0], dest_row_stride, 0, 1, false);
+ SingleChannelConvolveY1D(&intermediate[0], dest_row_stride, 0, 1,
+ filter, image_size,
+ &control[0], dest_row_stride, 0, 1, false);
+
+ // Now try the same using the other method.
+ RecursiveFilter recursive_filter(kernel_sigma, RecursiveFilter::FUNCTION);
+ SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &intermediate2[0], dest_row_stride,
+ 0, 1, false);
+ SingleChannelRecursiveGaussianX(&intermediate2[0], dest_row_stride, 0, 1,
+ recursive_filter, image_size,
+ &output[0], dest_row_stride, 0, 1, false);
+
+ // We cannot expect the results to be really the same. In particular,
+ // the standard implementation is computed in completely fixed-point, while
+ // recursive is done in floating point and squeezed back into char*. On top
+ // of that, its characteristics are a bit different (consult the paper).
+ EXPECT_NEAR(std::accumulate(intermediate.begin(), intermediate.end(), 0),
+ std::accumulate(intermediate2.begin(), intermediate2.end(), 0),
+ 50);
+ int difference_count = 0;
+ std::vector<unsigned char>::const_iterator i1, i2;
+ for (i1 = control.begin(), i2 = output.begin();
+ i1 != control.end(); ++i1, ++i2) {
+ if ((*i1 != 0) != (*i2 != 0))
+ difference_count++;
+ }
+
+ EXPECT_LE(difference_count, 44); // 44 is 2 * PI * r (r == 7, spot size).
+}
+
+TEST(RecursiveGaussian, SmoothingImpulse) {
+ static const int kImgWidth = 200;
+ static const int kImgHeight = 300;
+ static const int kChannelIndex = 3;
+ static const int kChannelCount = 3;
+ static const int kStrideSlack = 22;
+
+ std::vector<unsigned char> input;
+ SkISize image_size = SkISize::Make(kImgWidth, kImgHeight);
+ const SkIPoint centre_point = MakeImpulseImage(
+ &input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount,
+ kStrideSlack);
+
+ // Destination will be a single channel image with stide matching width.
+ const int dest_row_stride = kImgWidth;
+ const int dest_byte_count = dest_row_stride * kImgHeight;
+ std::vector<unsigned char> intermediate(dest_byte_count);
+ std::vector<unsigned char> output(dest_byte_count);
+
+ const int src_row_stride = ComputeRowStride(
+ kImgWidth, kChannelCount, kStrideSlack);
+
+ const float kernel_sigma = 5.0f;
+ RecursiveFilter recursive_filter(kernel_sigma, RecursiveFilter::FUNCTION);
+ SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &intermediate[0], dest_row_stride,
+ 0, 1, false);
+ SingleChannelRecursiveGaussianX(&intermediate[0], dest_row_stride, 0, 1,
+ recursive_filter, image_size,
+ &output[0], dest_row_stride, 0, 1, false);
+
+ // Check we got the expected impulse response.
+ const int cx = centre_point.x();
+ const int cy = centre_point.y();
+ unsigned char value_x = output[dest_row_stride * cy + cx];
+ unsigned char value_y = value_x;
+ EXPECT_GT(value_x, 0);
+ for (int offset = 0;
+ offset < std::min(kImgWidth, kImgHeight) && (value_y > 0 || value_x > 0);
+ ++offset) {
+ // Symmetricity and monotonicity along X.
+ EXPECT_EQ(output[dest_row_stride * cy + cx - offset],
+ output[dest_row_stride * cy + cx + offset]);
+ EXPECT_LE(output[dest_row_stride * cy + cx - offset], value_x);
+ value_x = output[dest_row_stride * cy + cx - offset];
+
+ // Symmetricity and monotonicity along Y.
+ EXPECT_EQ(output[dest_row_stride * (cy - offset) + cx],
+ output[dest_row_stride * (cy + offset) + cx]);
+ EXPECT_LE(output[dest_row_stride * (cy - offset) + cx], value_y);
+ value_y = output[dest_row_stride * (cy - offset) + cx];
+
+ // Symmetricity along X/Y (not really assured, but should be close).
+ EXPECT_NEAR(value_x, value_y, 1);
+ }
+
+ // Smooth the inverse now.
+ std::vector<unsigned char> output2(dest_byte_count);
+ std::transform(input.begin(), input.end(), input.begin(),
+ std::bind1st(std::minus<unsigned char>(), 255U));
+ SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &intermediate[0], dest_row_stride,
+ 0, 1, false);
+ SingleChannelRecursiveGaussianX(&intermediate[0], dest_row_stride, 0, 1,
+ recursive_filter, image_size,
+ &output2[0], dest_row_stride, 0, 1, false);
+ // The image should be the reverse of output, but permitting for rounding
+ // we will only claim that wherever output is 0, output2 should be 255.
+ // There still can be differences at the edges of the object.
+ std::vector<unsigned char>::const_iterator i1, i2;
+ int difference_count = 0;
+ for (i1 = output.begin(), i2 = output2.begin();
+ i1 != output.end(); ++i1, ++i2) {
+ // The line below checks (*i1 == 0 <==> *i2 == 255).
+ if ((*i1 != 0 && *i2 == 255) && ! (*i1 == 0 && *i2 != 255))
+ ++difference_count;
+ }
+ EXPECT_LE(difference_count, 8);
+}
+
+TEST(RecursiveGaussian, FirstDerivative) {
+ static const int kImgWidth = 512;
+ static const int kImgHeight = 1024;
+ static const int kChannelIndex = 2;
+ static const int kChannelCount = 4;
+ static const int kStrideSlack = 22;
+ static const int kBoxSize = 400;
+
+ std::vector<unsigned char> input;
+ const SkISize image_size = SkISize::Make(kImgWidth, kImgHeight);
+ const SkIRect box = MakeBoxImage(
+ &input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount,
+ kStrideSlack, kBoxSize, kBoxSize, 200);
+
+ // Destination will be a single channel image with stide matching width.
+ const int dest_row_stride = kImgWidth;
+ const int dest_byte_count = dest_row_stride * kImgHeight;
+ std::vector<unsigned char> output_x(dest_byte_count);
+ std::vector<unsigned char> output_y(dest_byte_count);
+ std::vector<unsigned char> output(dest_byte_count);
+
+ const int src_row_stride = ComputeRowStride(
+ kImgWidth, kChannelCount, kStrideSlack);
+
+ const float kernel_sigma = 3.0f;
+ const int spread = 4 * kernel_sigma;
+ RecursiveFilter recursive_filter(kernel_sigma,
+ RecursiveFilter::FIRST_DERIVATIVE);
+ SingleChannelRecursiveGaussianX(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &output_x[0], dest_row_stride,
+ 0, 1, true);
+ SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &output_y[0], dest_row_stride,
+ 0, 1, true);
+
+ // In test code we can assume adding the two up should do fine.
+ std::vector<unsigned char>::const_iterator ix, iy;
+ std::vector<unsigned char>::iterator target;
+ for (target = output.begin(), ix = output_x.begin(), iy = output_y.begin();
+ target < output.end(); ++target, ++ix, ++iy) {
+ *target = *ix + *iy;
+ }
+
+ SkIRect inflated_rect(box);
+ inflated_rect.outset(spread, spread);
+ SkIRect deflated_rect(box);
+ deflated_rect.inset(spread, spread);
+
+ int image_total = ComputeBoxSum(output,
+ SkIRect::MakeWH(kImgWidth, kImgHeight),
+ kImgWidth);
+ int box_inflated = ComputeBoxSum(output, inflated_rect, kImgWidth);
+ int box_deflated = ComputeBoxSum(output, deflated_rect, kImgWidth);
+ EXPECT_EQ(box_deflated, 0);
+ EXPECT_EQ(image_total, box_inflated);
+
+ // Try inverted image. Behaviour should be very similar (modulo rounding).
+ std::transform(input.begin(), input.end(), input.begin(),
+ std::bind1st(std::minus<unsigned char>(), 255U));
+ SingleChannelRecursiveGaussianX(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &output_x[0], dest_row_stride,
+ 0, 1, true);
+ SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &output_y[0], dest_row_stride,
+ 0, 1, true);
+
+ for (target = output.begin(), ix = output_x.begin(), iy = output_y.begin();
+ target < output.end(); ++target, ++ix, ++iy) {
+ *target = *ix + *iy;
+ }
+
+ image_total = ComputeBoxSum(output,
+ SkIRect::MakeWH(kImgWidth, kImgHeight),
+ kImgWidth);
+ box_inflated = ComputeBoxSum(output, inflated_rect, kImgWidth);
+ box_deflated = ComputeBoxSum(output, deflated_rect, kImgWidth);
+
+ EXPECT_EQ(box_deflated, 0);
+ EXPECT_EQ(image_total, box_inflated);
+}
+
+TEST(RecursiveGaussian, SecondDerivative) {
+ static const int kImgWidth = 700;
+ static const int kImgHeight = 500;
+ static const int kChannelIndex = 0;
+ static const int kChannelCount = 2;
+ static const int kStrideSlack = 42;
+ static const int kBoxSize = 200;
+
+ std::vector<unsigned char> input;
+ SkISize image_size = SkISize::Make(kImgWidth, kImgHeight);
+ const SkIRect box = MakeBoxImage(
+ &input, kImgWidth, kImgHeight, kChannelIndex, kChannelCount,
+ kStrideSlack, kBoxSize, kBoxSize, 200);
+
+ // Destination will be a single channel image with stide matching width.
+ const int dest_row_stride = kImgWidth;
+ const int dest_byte_count = dest_row_stride * kImgHeight;
+ std::vector<unsigned char> output_x(dest_byte_count);
+ std::vector<unsigned char> output_y(dest_byte_count);
+ std::vector<unsigned char> output(dest_byte_count);
+
+ const int src_row_stride = ComputeRowStride(
+ kImgWidth, kChannelCount, kStrideSlack);
+
+ const float kernel_sigma = 5.0f;
+ const int spread = 8 * kernel_sigma;
+ RecursiveFilter recursive_filter(kernel_sigma,
+ RecursiveFilter::SECOND_DERIVATIVE);
+ SingleChannelRecursiveGaussianX(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &output_x[0], dest_row_stride,
+ 0, 1, true);
+ SingleChannelRecursiveGaussianY(&input[0], src_row_stride,
+ kChannelIndex, kChannelCount,
+ recursive_filter, image_size,
+ &output_y[0], dest_row_stride,
+ 0, 1, true);
+
+ // In test code we can assume adding the two up should do fine.
+ std::vector<unsigned char>::const_iterator ix, iy;
+ std::vector<unsigned char>::iterator target;
+ for (target = output.begin(),ix = output_x.begin(), iy = output_y.begin();
+ target < output.end(); ++target, ++ix, ++iy) {
+ *target = *ix + *iy;
+ }
+
+ int image_total = ComputeBoxSum(output,
+ SkIRect::MakeWH(kImgWidth, kImgHeight),
+ kImgWidth);
+ int box_inflated = ComputeBoxSum(output,
+ SkIRect::MakeLTRB(box.left() - spread,
+ box.top() - spread,
+ box.right() + spread,
+ box.bottom() + spread),
+ kImgWidth);
+ int box_deflated = ComputeBoxSum(output,
+ SkIRect::MakeLTRB(box.left() + spread,
+ box.top() + spread,
+ box.right() - spread,
+ box.bottom() - spread),
+ kImgWidth);
+ // Since second derivative is not really used and implemented mostly
+ // for the sake of completeness, we do not verify the detail (that dip
+ // in the middle). But it is there.
+ EXPECT_EQ(box_deflated, 0);
+ EXPECT_EQ(image_total, box_inflated);
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/refptr.h b/chromium/skia/ext/refptr.h
new file mode 100644
index 00000000000..a3900f61ba4
--- /dev/null
+++ b/chromium/skia/ext/refptr.h
@@ -0,0 +1,118 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_REFPTR_H_
+#define SKIA_EXT_REFPTR_H_
+
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace skia {
+
+// When creating/receiving a ref-counted pointer from Skia, wrap that pointer in
+// this class to avoid dealing with the ref-counting and prevent leaks/crashes
+// due to ref-counting bugs.
+//
+// Example of creating a new SkShader* and setting it on a SkPaint:
+// skia::RefPtr<SkShader> shader = skia::AdoptRef(SkGradientShader::Create());
+// paint.setShader(shader.get());
+//
+// When passing around a ref-counted pointer to methods outside of Skia, always
+// pass around the skia::RefPtr instead of the raw pointer. An example method
+// that takes a SkShader* parameter and saves the SkShader* in the class.
+// void AMethodThatSavesAShader(const skia::RefPtr<SkShader>& shader) {
+// member_refptr_ = shader;
+// }
+// skia::RefPtr<SkShader> member_refptr_;
+//
+// When returning a ref-counted pointer, also return the skia::RefPtr instead.
+// An example method that creates an SkShader* and returns it:
+// skia::RefPtr<SkShader> MakeAShader() {
+// return skia::AdoptRef(SkGradientShader::Create());
+// }
+//
+// To take a scoped reference to an object whose references are all owned
+// by other objects (i.e. does not have one that needs to be adopted) use the
+// skia::SharePtr helper:
+//
+// skia::RefPtr<SkShader> shader = skia::SharePtr(paint.getShader());
+//
+// Never call ref() or unref() on the underlying ref-counted pointer. If you
+// AdoptRef() the raw pointer immediately into a skia::RefPtr and always work
+// with skia::RefPtr instances instead, the ref-counting will be taken care of
+// for you.
+template<typename T>
+class RefPtr {
+ public:
+ RefPtr() : ptr_(NULL) {}
+
+ RefPtr(const RefPtr& other)
+ : ptr_(other.get()) {
+ SkSafeRef(ptr_);
+ }
+
+ template<typename U>
+ RefPtr(const RefPtr<U>& other)
+ : ptr_(other.get()) {
+ SkSafeRef(ptr_);
+ }
+
+ ~RefPtr() {
+ clear();
+ }
+
+ RefPtr& operator=(const RefPtr& other) {
+ SkRefCnt_SafeAssign(ptr_, other.get());
+ return *this;
+ }
+
+ template<typename U>
+ RefPtr& operator=(const RefPtr<U>& other) {
+ SkRefCnt_SafeAssign(ptr_, other.get());
+ return *this;
+ }
+
+ void clear() {
+ T* to_unref = ptr_;
+ ptr_ = NULL;
+ SkSafeUnref(to_unref);
+ }
+
+ T* get() const { return ptr_; }
+ T& operator*() const { return *ptr_; }
+ T* operator->() const { return ptr_; }
+
+ typedef T* RefPtr::*unspecified_bool_type;
+ operator unspecified_bool_type() const {
+ return ptr_ ? &RefPtr::ptr_ : NULL;
+ }
+
+ private:
+ T* ptr_;
+
+ // This function cannot be public because Skia starts its ref-counted
+ // objects at refcnt=1. This makes it impossible to differentiate
+ // between a newly created object (that doesn't need to be ref'd) or an
+ // already existing object with one owner (that does need to be ref'd so that
+ // this RefPtr can also manage its lifetime).
+ explicit RefPtr(T* ptr) : ptr_(ptr) {}
+
+ template<typename U>
+ friend RefPtr<U> AdoptRef(U* ptr);
+
+ template<typename U>
+ friend RefPtr<U> SharePtr(U* ptr);
+};
+
+// For objects that have an unowned reference (such as newly created objects).
+template<typename T>
+RefPtr<T> AdoptRef(T* ptr) { return RefPtr<T>(ptr); }
+
+// For objects that are already owned. This doesn't take ownership of existing
+// references and adds a new one.
+template<typename T>
+RefPtr<T> SharePtr(T* ptr) { return RefPtr<T>(SkSafeRef(ptr)); }
+
+} // namespace skia
+
+#endif // SKIA_EXT_REFPTR_H_
diff --git a/chromium/skia/ext/refptr_unittest.cc b/chromium/skia/ext/refptr_unittest.cc
new file mode 100644
index 00000000000..1d63ed1b1e9
--- /dev/null
+++ b/chromium/skia/ext/refptr_unittest.cc
@@ -0,0 +1,125 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/refptr.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace skia {
+namespace {
+
+TEST(RefPtrTest, ReferenceCounting) {
+ SkRefCnt* ref = new SkRefCnt();
+ EXPECT_EQ(1, ref->getRefCnt());
+
+ {
+ // Adopt the reference from the caller on creation.
+ RefPtr<SkRefCnt> refptr1 = AdoptRef(ref);
+ EXPECT_EQ(1, ref->getRefCnt());
+ EXPECT_EQ(1, refptr1->getRefCnt());
+
+ EXPECT_EQ(ref, &*refptr1);
+ EXPECT_EQ(ref, refptr1.get());
+
+ {
+ // Take a second reference for the second instance.
+ RefPtr<SkRefCnt> refptr2(refptr1);
+ EXPECT_EQ(2, ref->getRefCnt());
+
+ RefPtr<SkRefCnt> refptr3;
+ EXPECT_FALSE(refptr3);
+
+ // Take a third reference for the third instance.
+ refptr3 = refptr1;
+ EXPECT_EQ(3, ref->getRefCnt());
+
+ // Same object, so should have the same refcount.
+ refptr2 = refptr3;
+ EXPECT_EQ(3, ref->getRefCnt());
+
+ // Drop the object from refptr2, so it should lose its reference.
+ EXPECT_TRUE(refptr2);
+ refptr2.clear();
+ EXPECT_EQ(2, ref->getRefCnt());
+
+ EXPECT_FALSE(refptr2);
+ EXPECT_EQ(NULL, refptr2.get());
+
+ EXPECT_TRUE(refptr3);
+ EXPECT_EQ(2, refptr3->getRefCnt());
+ EXPECT_EQ(ref, &*refptr3);
+ EXPECT_EQ(ref, refptr3.get());
+ }
+
+ // Drop a reference when the third object is destroyed.
+ EXPECT_EQ(1, ref->getRefCnt());
+ }
+}
+
+TEST(RefPtrTest, Construct) {
+ SkRefCnt* ref = new SkRefCnt();
+ EXPECT_EQ(1, ref->getRefCnt());
+
+ // Adopt the reference from the caller on creation.
+ RefPtr<SkRefCnt> refptr1(AdoptRef(ref));
+ EXPECT_EQ(1, ref->getRefCnt());
+ EXPECT_EQ(1, refptr1->getRefCnt());
+
+ EXPECT_EQ(ref, &*refptr1);
+ EXPECT_EQ(ref, refptr1.get());
+
+ RefPtr<SkRefCnt> refptr2(refptr1);
+ EXPECT_EQ(2, ref->getRefCnt());
+}
+
+TEST(RefPtrTest, DeclareAndAssign) {
+ SkRefCnt* ref = new SkRefCnt();
+ EXPECT_EQ(1, ref->getRefCnt());
+
+ // Adopt the reference from the caller on creation.
+ RefPtr<SkRefCnt> refptr1 = AdoptRef(ref);
+ EXPECT_EQ(1, ref->getRefCnt());
+ EXPECT_EQ(1, refptr1->getRefCnt());
+
+ EXPECT_EQ(ref, &*refptr1);
+ EXPECT_EQ(ref, refptr1.get());
+
+ RefPtr<SkRefCnt> refptr2 = refptr1;
+ EXPECT_EQ(2, ref->getRefCnt());
+}
+
+TEST(RefPtrTest, Assign) {
+ SkRefCnt* ref = new SkRefCnt();
+ EXPECT_EQ(1, ref->getRefCnt());
+
+ // Adopt the reference from the caller on creation.
+ RefPtr<SkRefCnt> refptr1;
+ refptr1 = AdoptRef(ref);
+ EXPECT_EQ(1, ref->getRefCnt());
+ EXPECT_EQ(1, refptr1->getRefCnt());
+
+ EXPECT_EQ(ref, &*refptr1);
+ EXPECT_EQ(ref, refptr1.get());
+
+ RefPtr<SkRefCnt> refptr2;
+ refptr2 = refptr1;
+ EXPECT_EQ(2, ref->getRefCnt());
+}
+
+class Subclass : public SkRefCnt {};
+
+TEST(RefPtrTest, Upcast) {
+ RefPtr<Subclass> child = AdoptRef(new Subclass());
+ EXPECT_EQ(1, child->getRefCnt());
+
+ RefPtr<SkRefCnt> parent = child;
+ EXPECT_TRUE(child);
+ EXPECT_TRUE(parent);
+
+ EXPECT_EQ(2, child->getRefCnt());
+ EXPECT_EQ(2, parent->getRefCnt());
+}
+
+} // namespace
+} // namespace skia
diff --git a/chromium/skia/ext/skia_trace_shim.h b/chromium/skia/ext/skia_trace_shim.h
new file mode 100644
index 00000000000..62bdecc4ce7
--- /dev/null
+++ b/chromium/skia/ext/skia_trace_shim.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_SKIA_TRACE_SHIM_H_
+#define SKIA_EXT_SKIA_TRACE_SHIM_H_
+
+#include "base/debug/trace_event.h"
+
+#define SK_TRACE_EVENT0(name) \
+ TRACE_EVENT0("skia", name)
+#define SK_TRACE_EVENT1(name, arg1_name, arg1_val) \
+ TRACE_EVENT1("skia", name, arg1_name, arg1_val)
+#define SK_TRACE_EVENT2(name, arg1_name, arg1_val, arg2_name, arg2_val) \
+ TRACE_EVENT1("skia", name, arg1_name, arg1_val, arg2_name, arg2_val)
+
+#endif
diff --git a/chromium/skia/ext/skia_utils_base.cc b/chromium/skia/ext/skia_utils_base.cc
new file mode 100644
index 00000000000..07c06bb60d9
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_base.cc
@@ -0,0 +1,53 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/skia_utils_base.h"
+
+namespace skia {
+
+bool ReadSkString(const Pickle& pickle, PickleIterator* iter, SkString* str) {
+ int reply_length;
+ const char* reply_text;
+
+ if (!pickle.ReadData(iter, &reply_text, &reply_length))
+ return false;
+
+ if (str)
+ str->set(reply_text, reply_length);
+ return true;
+}
+
+bool ReadSkFontIdentity(const Pickle& pickle, PickleIterator* iter,
+ SkFontConfigInterface::FontIdentity* identity) {
+ uint32_t reply_id;
+ uint32_t reply_ttcIndex;
+ int reply_length;
+ const char* reply_text;
+
+ if (!pickle.ReadUInt32(iter, &reply_id) ||
+ !pickle.ReadUInt32(iter, &reply_ttcIndex) ||
+ !pickle.ReadData(iter, &reply_text, &reply_length))
+ return false;
+
+ if (identity) {
+ identity->fID = reply_id;
+ identity->fTTCIndex = reply_ttcIndex;
+ identity->fString.set(reply_text, reply_length);
+ }
+ return true;
+}
+
+bool WriteSkString(Pickle* pickle, const SkString& str) {
+ return pickle->WriteData(str.c_str(), str.size());
+}
+
+bool WriteSkFontIdentity(Pickle* pickle,
+ const SkFontConfigInterface::FontIdentity& identity) {
+ return pickle->WriteUInt32(identity.fID) &&
+ pickle->WriteUInt32(identity.fTTCIndex) &&
+ WriteSkString(pickle, identity.fString);
+}
+
+} // namespace skia
+
diff --git a/chromium/skia/ext/skia_utils_base.h b/chromium/skia/ext/skia_utils_base.h
new file mode 100644
index 00000000000..cfddd31b6f6
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_base.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_SKIA_UTILS_BASE_H_
+#define SKIA_EXT_SKIA_UTILS_BASE_H_
+
+#include "base/pickle.h"
+#include "third_party/skia/include/ports/SkFontConfigInterface.h"
+
+namespace skia {
+
+// Return true if the pickle/iterator contains a string. If so, and if str
+// is not null, copy that string into str.
+SK_API bool ReadSkString(const Pickle& pickle, PickleIterator* iter,
+ SkString* str);
+
+// Return true if the pickle/iterator contains a FontIdentity. If so, and if
+// identity is not null, copy it into identity.
+SK_API bool ReadSkFontIdentity(const Pickle& pickle, PickleIterator* iter,
+ SkFontConfigInterface::FontIdentity* identity);
+
+// Return true if str can be written into the request pickle.
+SK_API bool WriteSkString(Pickle* pickle, const SkString& str);
+
+// Return true if identity can be written into the request pickle.
+SK_API bool WriteSkFontIdentity(Pickle* pickle,
+ const SkFontConfigInterface::FontIdentity& identity);
+
+} // namespace skia
+
+#endif // SKIA_EXT_SKIA_UTILS_BASE_H_
+
diff --git a/chromium/skia/ext/skia_utils_ios.h b/chromium/skia/ext/skia_utils_ios.h
new file mode 100644
index 00000000000..6a854ce6aff
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_ios.h
@@ -0,0 +1,40 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_SKIA_UTILS_IOS_H_
+#define SKIA_EXT_SKIA_UTILS_IOS_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <vector>
+
+#include "third_party/skia/include/core/SkBitmap.h"
+
+#ifdef __OBJC__
+@class UIImage;
+@class NSData;
+#else
+class UIImage;
+class NSData;
+#endif
+
+namespace gfx {
+
+// Draws a CGImage into an SkBitmap of the given size.
+SK_API SkBitmap CGImageToSkBitmap(CGImageRef image,
+ CGSize size,
+ bool is_opaque);
+
+// Given an SkBitmap and a color space, return an autoreleased UIImage.
+SK_API UIImage* SkBitmapToUIImageWithColorSpace(const SkBitmap& skia_bitmap,
+ CGFloat scale,
+ CGColorSpaceRef color_space);
+
+// Decodes all image representations inside the data into a vector of SkBitmaps.
+// Returns a vector of all the successfully decoded representations or an empty
+// vector if none can be decoded.
+SK_API std::vector<SkBitmap> ImageDataToSkBitmaps(NSData* image_data);
+
+} // namespace gfx
+
+#endif // SKIA_EXT_SKIA_UTILS_IOS_H_
diff --git a/chromium/skia/ext/skia_utils_ios.mm b/chromium/skia/ext/skia_utils_ios.mm
new file mode 100644
index 00000000000..80c50fdec21
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_ios.mm
@@ -0,0 +1,103 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/skia_utils_ios.h"
+
+#import <ImageIO/ImageIO.h>
+#import <UIKit/UIKit.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "third_party/skia/include/utils/mac/SkCGUtils.h"
+
+namespace gfx {
+
+SkBitmap CGImageToSkBitmap(CGImageRef image, CGSize size, bool is_opaque) {
+ SkBitmap bitmap;
+ if (!image)
+ return bitmap;
+
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width, size.height);
+ if (!bitmap.allocPixels())
+ return bitmap;
+
+ bitmap.setIsOpaque(is_opaque);
+ void* data = bitmap.getPixels();
+
+ // Allocate a bitmap context with 4 components per pixel (BGRA). Apple
+ // recommends these flags for improved CG performance.
+#define HAS_ARGB_SHIFTS(a, r, g, b) \
+ (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
+ && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
+#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
+ base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateDeviceRGB());
+ base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
+ data,
+ size.width,
+ size.height,
+ 8,
+ size.width * 4,
+ color_space,
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
+#else
+#error We require that Skia's and CoreGraphics's recommended \
+ image memory layout match.
+#endif
+#undef HAS_ARGB_SHIFTS
+
+ DCHECK(context);
+ if (!context)
+ return bitmap;
+
+ CGRect imageRect = CGRectMake(0.0, 0.0, size.width, size.height);
+ CGContextSetBlendMode(context, kCGBlendModeCopy);
+ CGContextDrawImage(context, imageRect, image);
+
+ return bitmap;
+}
+
+UIImage* SkBitmapToUIImageWithColorSpace(const SkBitmap& skia_bitmap,
+ CGFloat scale,
+ CGColorSpaceRef color_space) {
+ if (skia_bitmap.isNull())
+ return nil;
+
+ // First convert SkBitmap to CGImageRef.
+ base::ScopedCFTypeRef<CGImageRef> cg_image(
+ SkCreateCGImageRefWithColorspace(skia_bitmap, color_space));
+
+ // Now convert to UIImage.
+ return [UIImage imageWithCGImage:cg_image.get()
+ scale:scale
+ orientation:UIImageOrientationUp];
+}
+
+std::vector<SkBitmap> ImageDataToSkBitmaps(NSData* image_data) {
+ DCHECK(image_data);
+ base::ScopedCFTypeRef<CFDictionaryRef> empty_dictionary(
+ CFDictionaryCreate(NULL, NULL, NULL, 0, NULL, NULL));
+ std::vector<SkBitmap> frames;
+
+ base::ScopedCFTypeRef<CGImageSourceRef> source(
+ CGImageSourceCreateWithData((CFDataRef)image_data, empty_dictionary));
+
+ size_t count = CGImageSourceGetCount(source);
+ for (size_t index = 0; index < count; ++index) {
+ base::ScopedCFTypeRef<CGImageRef> cg_image(
+ CGImageSourceCreateImageAtIndex(source, index, empty_dictionary));
+
+ CGSize size = CGSizeMake(CGImageGetWidth(cg_image),
+ CGImageGetHeight(cg_image));
+ const SkBitmap bitmap = CGImageToSkBitmap(cg_image, size, false);
+ if (!bitmap.empty())
+ frames.push_back(bitmap);
+ }
+
+ DLOG_IF(WARNING, frames.size() != count) << "Only decoded " << frames.size()
+ << " frames for " << count << " expected.";
+ return frames;
+}
+
+} // namespace gfx
diff --git a/chromium/skia/ext/skia_utils_ios_unittest.mm b/chromium/skia/ext/skia_utils_ios_unittest.mm
new file mode 100644
index 00000000000..c26e688291b
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_ios_unittest.mm
@@ -0,0 +1,157 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+#include "base/base64.h"
+#include "skia/ext/skia_utils_ios.mm"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+typedef testing::Test SkiaUtilsIosTest;
+
+// This is a base64 encoded version of google.com ico file. generated by
+// curl http://www.google.com/favicon.ico | base64 -b 76
+const char kIcoEncodedData[] =
+ "AAABAAIAEBAAAAEAIABoBAAAJgAAACAgAAABACAAqBAAAI4EAAAoAAAAEAAAACAAAAABACAAAAAA"
+ "AAAEAAASCwAAEgsAAAAAAAAAAAAA9IVCSvSFQuf0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hULk9IVCSvSFQub0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQuf0hUL/9IVC//SFQv/0hUL/9Y1O//rI"
+ "q//+7+f//eXX//vUvf/7z7X/96Fu//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//vYwv/97OH/9ZRZ//SFQv/0hUL/9IhG//zbx//3om7/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/97uX/+buW//SFQv/0hUL/9IVC//SFQv/5upT/+9O6//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/+b6b//zezP/0iEf/9IVC//SFQv/1klf//ezh//vPtP/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/3qXr/+siq//m8lv/5wqD//vTu//3t4//1klb/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0h0b//vbx//zi0//1j1H/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/2nmn/+bmS/////v/4"
+ "sIX/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/5uJH///v5//eo"
+ "ef/1jU//+82y//afav/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL//vXw"
+ "//vOs//0hUL/9IVC//ekcf/96+D/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//728v/4sIX/9IVC//SFQv/4s4n///v4//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/6yKn/+byX//SFQv/0hkT//eTV//vWv//0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IZE//m6lP/5u5b//OHQ///+/f/6y6//96d3//SFQv/0hUL/9IVC"
+ "//SFQv/0hULm9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hULm9IVCSfSFQub0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hULm9IVCSQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAIAAAAEAAAAABACAAAAAAAAAQAAASCwAAEgsA"
+ "AAAAAAAAAAAA9IVCAPSFQif0hUKt9IVC8vSFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQvL0hUKt9IVCJ/SFQgD0hUIo9IVC7/SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hULv9IVCKPSFQq30hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUKt9IVC8fSF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQvP0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9YtL//i2jv/8"
+ "28f//vLr///7+P///Pv//vTu//3n2v/6zbH/96Nw//SFQ//0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//ekcv/+8+z////////////+9fD/+9K5//m9mf/4to7/+buV//vSuf/++PT//OPT//aYYP/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/2l13///r3/////////fv/+b2Z//SIRv/0hUL/9IVC//SFQv/0hUL/9IVC//WN"
+ "T//84M///vXv//aZYf/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//vPtP////////////i0i//0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//WQUv///Pr//OPU//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL//eTV///////+9O7/9IVD//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//3m2P//////9ppi//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/718H/"
+ "//////3s4f/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL//vDn///////4"
+ "soj/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//erff////////38//WTWP/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//iziv////////////iwhf/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//rMsP///////eXW//WSVv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/4sYb///z7/////////Pv/9ZFV//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//ixhv/+8Of//vn1"
+ "//rMsP/4rH//9plh//WQUv/1j1L/+s2x//////////////////m9mf/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SGQ//2nmn/+buW//vNsv/82sb//e3j/////////////////////v/5wZ//9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/83Mj////////////++fb/"
+ "+K+C//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9ZRZ////"
+ "/////////vTt//aaYv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/1lFr////////////6xqf/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//ehbf/70bj//end//3o2////v3///////3l1//0iEb/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/5wqD////////////96t7/96Z2//WOUP/2nWf//NvH//zcyP/1"
+ "i0z/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/96l6/////////////vLr//WPUf/0hUL/9IVC"
+ "//SFQv/0h0b//end//3k1f/0iUn/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/8387////////////4"
+ "sYf/9IVC//SFQv/0hUL/9IVC//SFQv/6w6L///////nBn//0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "///69////////vj1//SIR//0hUL/9IVC//SFQv/0hUL/9IVC//m+mv///////e3j//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL///r3///////8387/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/+syw////"
+ "///++fb/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/95NX///////vUvP/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/97OH///////7y6//0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//i2jv///////N/O//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/96Nx////////////+s2x//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IdF//zh0P//+/j/9ZJW//SFQv/0hUL/9IVC//SKSv/96t7///////738v/1k1f/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9YxN//vUvf/96+D/96Z0//WNT//3om///ebY/////////Pv/+LKI"
+ "//WVW//0h0X/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//agbP/7zbL//enc//749P//"
+ "//////////////////////////3r4P/3p3f/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hULx9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC8/SFQq30hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0"
+ "hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUKt9IVCJ/SFQu/0hUL/9IVC//SFQv/0hUL/9IVC"
+ "//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/"
+ "9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC7/SFQif0hUIA9IVCJfSFQq30"
+ "hULx9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SF"
+ "Qv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC//SFQv/0hUL/9IVC8fSFQq30hUIl9IVC"
+ "AIAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ "AAAAAAAAAAAAAACAAAAB";
+
+NSData* IcoData() {
+ std::string output;
+ EXPECT_TRUE(base::Base64Decode(kIcoEncodedData, &output));
+ return [NSData dataWithBytes:output.data() length:output.length()];
+}
+
+NSData* InvalidData(NSUInteger length) {
+ NSMutableData* data=[NSMutableData dataWithLength:length];
+ memset([data mutableBytes], 0xFF, length);
+ return data;
+}
+
+TEST_F(SkiaUtilsIosTest, ImageDataToSkBitmaps) {
+ std::vector<SkBitmap> bitmaps(gfx::ImageDataToSkBitmaps(IcoData()));
+
+ EXPECT_EQ(2UL, bitmaps.size());
+ EXPECT_EQ(32, bitmaps[0].width());
+ EXPECT_EQ(32, bitmaps[0].height());
+ EXPECT_EQ(16, bitmaps[1].width());
+ EXPECT_EQ(16, bitmaps[1].height());
+}
+
+TEST_F(SkiaUtilsIosTest, InvalidDataFailure) {
+ std::vector<SkBitmap> bitmaps1(gfx::ImageDataToSkBitmaps(InvalidData(1)));
+ EXPECT_EQ(0UL, bitmaps1.size());
+ std::vector<SkBitmap> bitmaps2(gfx::ImageDataToSkBitmaps(InvalidData(10)));
+ EXPECT_EQ(0UL, bitmaps2.size());
+ std::vector<SkBitmap> bitmaps3(gfx::ImageDataToSkBitmaps(InvalidData(100)));
+ EXPECT_EQ(0UL, bitmaps3.size());
+ std::vector<SkBitmap> bitmaps4(gfx::ImageDataToSkBitmaps(InvalidData(1000)));
+ EXPECT_EQ(0UL, bitmaps4.size());
+ std::vector<SkBitmap> bitmaps5(gfx::ImageDataToSkBitmaps(InvalidData(5000)));
+ EXPECT_EQ(0UL, bitmaps5.size());
+}
+
+TEST_F(SkiaUtilsIosTest, EmptyDataFailure) {
+ std::vector<SkBitmap> bitmaps(gfx::ImageDataToSkBitmaps([NSData data]));
+
+ EXPECT_EQ(0UL, bitmaps.size());
+}
+
+} // namespace
+
diff --git a/chromium/skia/ext/skia_utils_mac.h b/chromium/skia/ext/skia_utils_mac.h
new file mode 100644
index 00000000000..e6f7b35bbc2
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_mac.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_SKIA_UTILS_MAC_H_
+#define SKIA_EXT_SKIA_UTILS_MAC_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <vector>
+
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkColor.h"
+
+struct SkIRect;
+struct SkPoint;
+struct SkRect;
+class SkCanvas;
+class SkMatrix;
+#ifdef __LP64__
+typedef CGSize NSSize;
+#else
+typedef struct _NSSize NSSize;
+#endif
+
+#ifdef __OBJC__
+@class NSBitmapImageRep;
+@class NSImage;
+@class NSImageRep;
+@class NSColor;
+#else
+class NSBitmapImageRep;
+class NSImage;
+class NSImageRep;
+class NSColor;
+#endif
+
+namespace gfx {
+
+// Converts a Skia point to a CoreGraphics CGPoint.
+// Both use same in-memory format.
+inline const CGPoint& SkPointToCGPoint(const SkPoint& point) {
+ return reinterpret_cast<const CGPoint&>(point);
+}
+
+// Converts a CoreGraphics point to a Skia CGPoint.
+// Both use same in-memory format.
+inline const SkPoint& CGPointToSkPoint(const CGPoint& point) {
+ return reinterpret_cast<const SkPoint&>(point);
+}
+
+// Matrix converters.
+SK_API CGAffineTransform SkMatrixToCGAffineTransform(const SkMatrix& matrix);
+
+// Rectangle converters.
+SK_API SkRect CGRectToSkRect(const CGRect& rect);
+
+// Converts a Skia rect to a CoreGraphics CGRect.
+CGRect SkIRectToCGRect(const SkIRect& rect);
+CGRect SkRectToCGRect(const SkRect& rect);
+
+// Converts CGColorRef to the ARGB layout Skia expects.
+SK_API SkColor CGColorRefToSkColor(CGColorRef color);
+
+// Converts ARGB to CGColorRef.
+SK_API CGColorRef CGColorCreateFromSkColor(SkColor color);
+
+// Converts NSColor to ARGB. Returns raw rgb values and does no colorspace
+// conversion. Only valid for colors in calibrated and device color spaces.
+SK_API SkColor NSDeviceColorToSkColor(NSColor* color);
+
+// Converts ARGB in the specified color space to NSColor.
+// Prefer sRGB over calibrated colors.
+SK_API NSColor* SkColorToCalibratedNSColor(SkColor color);
+SK_API NSColor* SkColorToDeviceNSColor(SkColor color);
+SK_API NSColor* SkColorToSRGBNSColor(SkColor color);
+
+// Converts a CGImage to a SkBitmap.
+SK_API SkBitmap CGImageToSkBitmap(CGImageRef image);
+
+// Draws an NSImage with a given size into a SkBitmap.
+SK_API SkBitmap NSImageToSkBitmap(NSImage* image, NSSize size, bool is_opaque);
+
+// Draws an NSImageRep with a given size into a SkBitmap.
+SK_API SkBitmap NSImageRepToSkBitmap(
+ NSImageRep* image, NSSize size, bool is_opaque);
+
+// Given an SkBitmap, return an autoreleased NSBitmapImageRep in the generic
+// color space.
+SK_API NSBitmapImageRep* SkBitmapToNSBitmapImageRep(const SkBitmap& image);
+
+SK_API NSBitmapImageRep* SkBitmapToNSBitmapImageRepWithColorSpace(
+ const SkBitmap& skiaBitmap,
+ CGColorSpaceRef colorSpace);
+
+// Given an SkBitmap and a color space, return an autoreleased NSImage.
+SK_API NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& icon,
+ CGColorSpaceRef colorSpace);
+
+// Given an SkBitmap, return an autoreleased NSImage in the generic color space.
+// DEPRECATED, use SkBitmapToNSImageWithColorSpace() instead.
+// TODO(thakis): Remove this -- http://crbug.com/69432
+SK_API NSImage* SkBitmapToNSImage(const SkBitmap& icon);
+
+// Converts a SkCanvas temporarily to a CGContext
+class SK_API SkiaBitLocker {
+ public:
+ explicit SkiaBitLocker(SkCanvas* canvas);
+ ~SkiaBitLocker();
+ CGContextRef cgContext();
+
+ private:
+ void releaseIfNeeded();
+ SkCanvas* canvas_;
+ CGContextRef cgContext_;
+ SkBitmap bitmap_;
+ bool useDeviceBits_;
+};
+
+
+} // namespace gfx
+
+#endif // SKIA_EXT_SKIA_UTILS_MAC_H_
diff --git a/chromium/skia/ext/skia_utils_mac.mm b/chromium/skia/ext/skia_utils_mac.mm
new file mode 100644
index 00000000000..213bb00e498
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_mac.mm
@@ -0,0 +1,429 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/skia_utils_mac.h"
+
+#import <AppKit/AppKit.h>
+
+#include "base/logging.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/memory/scoped_ptr.h"
+#include "skia/ext/bitmap_platform_device_mac.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/utils/mac/SkCGUtils.h"
+
+namespace {
+
+// Draws an NSImage or an NSImageRep with a given size into a SkBitmap.
+SkBitmap NSImageOrNSImageRepToSkBitmap(
+ NSImage* image,
+ NSImageRep* image_rep,
+ NSSize size,
+ bool is_opaque) {
+ // Only image or image_rep should be provided, not both.
+ DCHECK((image != 0) ^ (image_rep != 0));
+
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width, size.height);
+ if (!bitmap.allocPixels())
+ return bitmap; // Return |bitmap| which should respond true to isNull().
+
+ bitmap.setIsOpaque(is_opaque);
+
+ base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+ void* data = bitmap.getPixels();
+
+ // Allocate a bitmap context with 4 components per pixel (BGRA). Apple
+ // recommends these flags for improved CG performance.
+#define HAS_ARGB_SHIFTS(a, r, g, b) \
+ (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
+ && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
+#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
+ base::ScopedCFTypeRef<CGContextRef> context(CGBitmapContextCreate(
+ data,
+ size.width,
+ size.height,
+ 8,
+ size.width * 4,
+ color_space,
+ kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
+#else
+#error We require that Skia's and CoreGraphics's recommended \
+ image memory layout match.
+#endif
+#undef HAS_ARGB_SHIFTS
+
+ // Something went really wrong. Best guess is that the bitmap data is invalid.
+ DCHECK(context);
+
+ [NSGraphicsContext saveGraphicsState];
+
+ NSGraphicsContext* context_cocoa =
+ [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
+ [NSGraphicsContext setCurrentContext:context_cocoa];
+
+ NSRect drawRect = NSMakeRect(0, 0, size.width, size.height);
+ if (image) {
+ [image drawInRect:drawRect
+ fromRect:NSZeroRect
+ operation:NSCompositeCopy
+ fraction:1.0];
+ } else {
+ [image_rep drawInRect:drawRect
+ fromRect:NSZeroRect
+ operation:NSCompositeCopy
+ fraction:1.0
+ respectFlipped:NO
+ hints:nil];
+ }
+
+ [NSGraphicsContext restoreGraphicsState];
+
+ return bitmap;
+}
+
+} // namespace
+
+namespace gfx {
+
+CGAffineTransform SkMatrixToCGAffineTransform(const SkMatrix& matrix) {
+ // CGAffineTransforms don't support perspective transforms, so make sure
+ // we don't get those.
+ DCHECK(matrix[SkMatrix::kMPersp0] == 0.0f);
+ DCHECK(matrix[SkMatrix::kMPersp1] == 0.0f);
+ DCHECK(matrix[SkMatrix::kMPersp2] == 1.0f);
+
+ return CGAffineTransformMake(matrix[SkMatrix::kMScaleX],
+ matrix[SkMatrix::kMSkewY],
+ matrix[SkMatrix::kMSkewX],
+ matrix[SkMatrix::kMScaleY],
+ matrix[SkMatrix::kMTransX],
+ matrix[SkMatrix::kMTransY]);
+}
+
+SkRect CGRectToSkRect(const CGRect& rect) {
+ SkRect sk_rect = {
+ rect.origin.x, rect.origin.y, CGRectGetMaxX(rect), CGRectGetMaxY(rect)
+ };
+ return sk_rect;
+}
+
+CGRect SkIRectToCGRect(const SkIRect& rect) {
+ CGRect cg_rect = {
+ { rect.fLeft, rect.fTop },
+ { rect.fRight - rect.fLeft, rect.fBottom - rect.fTop }
+ };
+ return cg_rect;
+}
+
+CGRect SkRectToCGRect(const SkRect& rect) {
+ CGRect cg_rect = {
+ { rect.fLeft, rect.fTop },
+ { rect.fRight - rect.fLeft, rect.fBottom - rect.fTop }
+ };
+ return cg_rect;
+}
+
+// Converts CGColorRef to the ARGB layout Skia expects.
+SkColor CGColorRefToSkColor(CGColorRef color) {
+ DCHECK(CGColorGetNumberOfComponents(color) == 4);
+ const CGFloat* components = CGColorGetComponents(color);
+ return SkColorSetARGB(SkScalarRound(255.0 * components[3]), // alpha
+ SkScalarRound(255.0 * components[0]), // red
+ SkScalarRound(255.0 * components[1]), // green
+ SkScalarRound(255.0 * components[2])); // blue
+}
+
+// Converts ARGB to CGColorRef.
+CGColorRef CGColorCreateFromSkColor(SkColor color) {
+ return CGColorCreateGenericRGB(SkColorGetR(color) / 255.0,
+ SkColorGetG(color) / 255.0,
+ SkColorGetB(color) / 255.0,
+ SkColorGetA(color) / 255.0);
+}
+
+// Converts NSColor to ARGB
+SkColor NSDeviceColorToSkColor(NSColor* color) {
+ DCHECK([color colorSpace] == [NSColorSpace genericRGBColorSpace] ||
+ [color colorSpace] == [NSColorSpace deviceRGBColorSpace]);
+ CGFloat red, green, blue, alpha;
+ color = [color colorUsingColorSpace:[NSColorSpace deviceRGBColorSpace]];
+ [color getRed:&red green:&green blue:&blue alpha:&alpha];
+ return SkColorSetARGB(SkScalarRound(255.0 * alpha),
+ SkScalarRound(255.0 * red),
+ SkScalarRound(255.0 * green),
+ SkScalarRound(255.0 * blue));
+}
+
+// Converts ARGB to NSColor.
+NSColor* SkColorToCalibratedNSColor(SkColor color) {
+ return [NSColor colorWithCalibratedRed:SkColorGetR(color) / 255.0
+ green:SkColorGetG(color) / 255.0
+ blue:SkColorGetB(color) / 255.0
+ alpha:SkColorGetA(color) / 255.0];
+}
+
+NSColor* SkColorToDeviceNSColor(SkColor color) {
+ return [NSColor colorWithDeviceRed:SkColorGetR(color) / 255.0
+ green:SkColorGetG(color) / 255.0
+ blue:SkColorGetB(color) / 255.0
+ alpha:SkColorGetA(color) / 255.0];
+}
+
+NSColor* SkColorToSRGBNSColor(SkColor color) {
+ const CGFloat components[] = {
+ SkColorGetR(color) / 255.0,
+ SkColorGetG(color) / 255.0,
+ SkColorGetB(color) / 255.0,
+ SkColorGetA(color) / 255.0
+ };
+ return [NSColor colorWithColorSpace:[NSColorSpace sRGBColorSpace]
+ components:components
+ count:4];
+}
+
+SkBitmap CGImageToSkBitmap(CGImageRef image) {
+ if (!image)
+ return SkBitmap();
+
+ int width = CGImageGetWidth(image);
+ int height = CGImageGetHeight(image);
+
+ scoped_ptr<SkDevice> device(
+ skia::BitmapPlatformDevice::Create(NULL, width, height, false));
+
+ CGContextRef context = skia::GetBitmapContext(device.get());
+
+ // We need to invert the y-axis of the canvas so that Core Graphics drawing
+ // happens right-side up. Skia has an upper-left origin and CG has a lower-
+ // left one.
+ CGContextScaleCTM(context, 1.0, -1.0);
+ CGContextTranslateCTM(context, 0, -height);
+
+ // We want to copy transparent pixels from |image|, instead of blending it
+ // onto uninitialized pixels.
+ CGContextSetBlendMode(context, kCGBlendModeCopy);
+
+ CGRect rect = CGRectMake(0, 0, width, height);
+ CGContextDrawImage(context, rect, image);
+
+ // Because |device| will be cleaned up and will take its pixels with it, we
+ // copy it to the stack and return it.
+ SkBitmap bitmap = device->accessBitmap(false);
+
+ return bitmap;
+}
+
+SkBitmap NSImageToSkBitmap(NSImage* image, NSSize size, bool is_opaque) {
+ return NSImageOrNSImageRepToSkBitmap(image, nil, size, is_opaque);
+}
+
+SkBitmap NSImageRepToSkBitmap(
+ NSImageRep* image_rep, NSSize size, bool is_opaque) {
+ return NSImageOrNSImageRepToSkBitmap(nil, image_rep, size, is_opaque);
+}
+
+NSBitmapImageRep* SkBitmapToNSBitmapImageRep(const SkBitmap& skiaBitmap) {
+ base::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+ return SkBitmapToNSBitmapImageRepWithColorSpace(skiaBitmap, color_space);
+}
+
+NSBitmapImageRep* SkBitmapToNSBitmapImageRepWithColorSpace(
+ const SkBitmap& skiaBitmap,
+ CGColorSpaceRef colorSpace) {
+ // First convert SkBitmap to CGImageRef.
+ base::ScopedCFTypeRef<CGImageRef> cgimage(
+ SkCreateCGImageRefWithColorspace(skiaBitmap, colorSpace));
+
+ // Now convert to NSBitmapImageRep.
+ base::scoped_nsobject<NSBitmapImageRep> bitmap(
+ [[NSBitmapImageRep alloc] initWithCGImage:cgimage]);
+ return [bitmap.release() autorelease];
+}
+
+NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& skiaBitmap,
+ CGColorSpaceRef colorSpace) {
+ if (skiaBitmap.isNull())
+ return nil;
+
+ base::scoped_nsobject<NSImage> image([[NSImage alloc] init]);
+ [image addRepresentation:
+ SkBitmapToNSBitmapImageRepWithColorSpace(skiaBitmap, colorSpace)];
+ [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())];
+ return [image.release() autorelease];
+}
+
+NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) {
+ base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+ return SkBitmapToNSImageWithColorSpace(skiaBitmap, colorSpace.get());
+}
+
+SkiaBitLocker::SkiaBitLocker(SkCanvas* canvas)
+ : canvas_(canvas),
+ cgContext_(0) {
+}
+
+SkiaBitLocker::~SkiaBitLocker() {
+ releaseIfNeeded();
+}
+
+// This must be called to balance calls to cgContext
+void SkiaBitLocker::releaseIfNeeded() {
+ if (!cgContext_)
+ return;
+ if (useDeviceBits_) {
+ bitmap_.unlockPixels();
+ } else {
+ // Find the bits that were drawn to.
+ SkAutoLockPixels lockedPixels(bitmap_);
+ const uint32_t* pixelBase
+ = reinterpret_cast<uint32_t*>(bitmap_.getPixels());
+ int rowPixels = bitmap_.rowBytesAsPixels();
+ int width = bitmap_.width();
+ int height = bitmap_.height();
+ SkIRect bounds;
+ bounds.fTop = 0;
+ int x;
+ int y = -1;
+ const uint32_t* pixels = pixelBase;
+ while (++y < height) {
+ for (x = 0; x < width; ++x) {
+ if (pixels[x]) {
+ bounds.fTop = y;
+ goto foundTop;
+ }
+ }
+ pixels += rowPixels;
+ }
+foundTop:
+ bounds.fBottom = height;
+ y = height;
+ pixels = pixelBase + rowPixels * (y - 1);
+ while (--y > bounds.fTop) {
+ for (x = 0; x < width; ++x) {
+ if (pixels[x]) {
+ bounds.fBottom = y + 1;
+ goto foundBottom;
+ }
+ }
+ pixels -= rowPixels;
+ }
+foundBottom:
+ bounds.fLeft = 0;
+ x = -1;
+ while (++x < width) {
+ pixels = pixelBase + rowPixels * bounds.fTop;
+ for (y = bounds.fTop; y < bounds.fBottom; ++y) {
+ if (pixels[x]) {
+ bounds.fLeft = x;
+ goto foundLeft;
+ }
+ pixels += rowPixels;
+ }
+ }
+foundLeft:
+ bounds.fRight = width;
+ x = width;
+ while (--x > bounds.fLeft) {
+ pixels = pixelBase + rowPixels * bounds.fTop;
+ for (y = bounds.fTop; y < bounds.fBottom; ++y) {
+ if (pixels[x]) {
+ bounds.fRight = x + 1;
+ goto foundRight;
+ }
+ pixels += rowPixels;
+ }
+ }
+foundRight:
+ SkBitmap subset;
+ if (!bitmap_.extractSubset(&subset, bounds)) {
+ return;
+ }
+ // Neutralize the global matrix by concatenating the inverse. In the
+ // future, Skia may provide some mechanism to set the device portion of
+ // the matrix to identity without clobbering any hosting matrix (e.g., the
+ // picture's matrix).
+ const SkMatrix& skMatrix = canvas_->getTotalMatrix();
+ SkMatrix inverse;
+ if (!skMatrix.invert(&inverse))
+ return;
+ canvas_->save();
+ canvas_->concat(inverse);
+ canvas_->drawBitmap(subset, bounds.fLeft, bounds.fTop);
+ canvas_->restore();
+ }
+ CGContextRelease(cgContext_);
+ cgContext_ = 0;
+}
+
+CGContextRef SkiaBitLocker::cgContext() {
+ SkDevice* device = canvas_->getTopDevice();
+ DCHECK(device);
+ if (!device)
+ return 0;
+ releaseIfNeeded(); // This flushes any prior bitmap use
+ const SkBitmap& deviceBits = device->accessBitmap(true);
+ useDeviceBits_ = deviceBits.getPixels();
+ if (useDeviceBits_) {
+ bitmap_ = deviceBits;
+ bitmap_.lockPixels();
+ } else {
+ bitmap_.setConfig(
+ SkBitmap::kARGB_8888_Config, deviceBits.width(), deviceBits.height());
+ bitmap_.allocPixels();
+ bitmap_.eraseColor(0);
+ }
+ base::ScopedCFTypeRef<CGColorSpaceRef> colorSpace(
+ CGColorSpaceCreateDeviceRGB());
+ cgContext_ = CGBitmapContextCreate(bitmap_.getPixels(), bitmap_.width(),
+ bitmap_.height(), 8, bitmap_.rowBytes(), colorSpace,
+ kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
+
+ // Apply device matrix.
+ CGAffineTransform contentsTransform = CGAffineTransformMakeScale(1, -1);
+ contentsTransform = CGAffineTransformTranslate(contentsTransform, 0,
+ -device->height());
+ CGContextConcatCTM(cgContext_, contentsTransform);
+
+ const SkIPoint& pt = device->getOrigin();
+ // Skip applying the clip when not writing directly to device.
+ // They're applied in the offscreen case when the bitmap is drawn.
+ if (useDeviceBits_) {
+ // Apply clip in device coordinates.
+ CGMutablePathRef clipPath = CGPathCreateMutable();
+ const SkRegion& clipRgn = canvas_->getTotalClip();
+ if (clipRgn.isEmpty()) {
+ // CoreGraphics does not consider a newly created path to be empty.
+ // Explicitly set it to empty so the subsequent drawing is clipped out.
+ // It would be better to make the CGContext hidden if there was a CG
+ // call that does that.
+ CGPathAddRect(clipPath, 0, CGRectMake(0, 0, 0, 0));
+ }
+ SkRegion::Iterator iter(clipRgn);
+ const SkIPoint& pt = device->getOrigin();
+ for (; !iter.done(); iter.next()) {
+ SkIRect skRect = iter.rect();
+ skRect.offset(-pt);
+ CGRect cgRect = SkIRectToCGRect(skRect);
+ CGPathAddRect(clipPath, 0, cgRect);
+ }
+ CGContextAddPath(cgContext_, clipPath);
+ CGContextClip(cgContext_);
+ CGPathRelease(clipPath);
+ }
+
+ // Apply content matrix.
+ SkMatrix skMatrix = canvas_->getTotalMatrix();
+ skMatrix.postTranslate(-SkIntToScalar(pt.fX), -SkIntToScalar(pt.fY));
+ CGAffineTransform affine = SkMatrixToCGAffineTransform(skMatrix);
+ CGContextConcatCTM(cgContext_, affine);
+
+ return cgContext_;
+}
+
+} // namespace gfx
diff --git a/chromium/skia/ext/skia_utils_mac_unittest.mm b/chromium/skia/ext/skia_utils_mac_unittest.mm
new file mode 100644
index 00000000000..51b957eb384
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_mac_unittest.mm
@@ -0,0 +1,246 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/skia_utils_mac.mm"
+
+#include "base/mac/scoped_nsobject.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class SkiaUtilsMacTest : public testing::Test {
+ public:
+ // Creates a red or blue bitmap.
+ SkBitmap CreateSkBitmap(int width, int height, bool isred, bool tfbit);
+
+ // Creates a red or blue image.
+ NSImage* CreateNSImage(int width, int height, bool isred);
+
+ // Checks that the given bitmap rep is actually red or blue.
+ void TestImageRep(NSBitmapImageRep* imageRep, bool isred);
+
+ // Checks that the given bitmap is actually red or blue.
+ void TestSkBitmap(const SkBitmap& bitmap, bool isred);
+
+ enum BitLockerTest {
+ TestIdentity = 0,
+ TestTranslate = 1,
+ TestClip = 2,
+ TestXClip = TestTranslate | TestClip,
+ TestNoBits = 4,
+ TestTranslateNoBits = TestTranslate | TestNoBits,
+ TestClipNoBits = TestClip | TestNoBits,
+ TestXClipNoBits = TestXClip | TestNoBits,
+ };
+ void RunBitLockerTest(BitLockerTest test);
+
+ // If not red, is blue.
+ // If not tfbit (twenty-four-bit), is 444.
+ void ShapeHelper(int width, int height, bool isred, bool tfbit);
+};
+
+SkBitmap SkiaUtilsMacTest::CreateSkBitmap(int width, int height,
+ bool isred, bool tfbit) {
+ SkBitmap bitmap;
+
+ if (tfbit)
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ else
+ bitmap.setConfig(SkBitmap::kARGB_4444_Config, width, height);
+ bitmap.allocPixels();
+
+ if (isred)
+ bitmap.eraseRGB(0xff, 0, 0);
+ else
+ bitmap.eraseRGB(0, 0, 0xff);
+
+ return bitmap;
+}
+
+NSImage* SkiaUtilsMacTest::CreateNSImage(int width, int height, bool isred) {
+ base::scoped_nsobject<NSImage> image(
+ [[NSImage alloc] initWithSize:NSMakeSize(width, height)]);
+ [image lockFocus];
+ if (isred)
+ [[NSColor colorWithDeviceRed:1.0 green:0.0 blue:0.0 alpha:1.0] set];
+ else
+ [[NSColor colorWithDeviceRed:0.0 green:0.0 blue:1.0 alpha:1.0] set];
+ NSRectFill(NSMakeRect(0, 0, width, height));
+ [image unlockFocus];
+ return [image.release() autorelease];
+}
+
+void SkiaUtilsMacTest::TestImageRep(NSBitmapImageRep* imageRep, bool isred) {
+ // Get the color of a pixel and make sure it looks fine
+ int x = [imageRep size].width > 17 ? 17 : 0;
+ int y = [imageRep size].height > 17 ? 17 : 0;
+ NSColor* color = [imageRep colorAtX:x y:y];
+ CGFloat red = 0, green = 0, blue = 0, alpha = 0;
+
+ // SkBitmapToNSImage returns a bitmap in the calibrated color space (sRGB),
+ // while NSReadPixel returns a color in the device color space. Convert back
+ // to the calibrated color space before testing.
+ color = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
+
+ [color getRed:&red green:&green blue:&blue alpha:&alpha];
+
+ // Be tolerant of floating point rounding and lossy color space conversions.
+ if (isred) {
+ EXPECT_GT(red, 0.95);
+ EXPECT_LT(blue, 0.05);
+ } else {
+ EXPECT_LT(red, 0.05);
+ EXPECT_GT(blue, 0.95);
+ }
+ EXPECT_LT(green, 0.05);
+ EXPECT_GT(alpha, 0.95);
+}
+
+void SkiaUtilsMacTest::TestSkBitmap(const SkBitmap& bitmap, bool isred) {
+ int x = bitmap.width() > 17 ? 17 : 0;
+ int y = bitmap.height() > 17 ? 17 : 0;
+ SkColor color = bitmap.getColor(x, y);
+
+ // Be tolerant of lossy color space conversions.
+ // TODO(sail): Fix color space conversion issues, http://crbug.com/79946
+ if (isred) {
+ EXPECT_GT(SkColorGetR(color), 245u);
+ EXPECT_LT(SkColorGetB(color), 10u);
+ } else {
+ EXPECT_LT(SkColorGetR(color), 10u);
+ EXPECT_GT(SkColorGetB(color), 245u);
+ }
+ EXPECT_LT(SkColorGetG(color), 10u);
+ EXPECT_GT(SkColorGetA(color), 245u);
+}
+
+// setBitmapDevice has been deprecated/removed. Is this test still useful?
+void SkiaUtilsMacTest::RunBitLockerTest(BitLockerTest test) {
+ const unsigned width = 2;
+ const unsigned height = 2;
+ const unsigned storageSize = width * height;
+ const unsigned original[] = {0xFF333333, 0xFF666666, 0xFF999999, 0xFFCCCCCC};
+ EXPECT_EQ(storageSize, sizeof(original) / sizeof(original[0]));
+ unsigned bits[storageSize];
+ memcpy(bits, original, sizeof(original));
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ bitmap.setPixels(bits);
+
+ SkCanvas canvas(bitmap);
+ if (test & TestTranslate)
+ canvas.translate(width / 2, 0);
+ if (test & TestClip) {
+ SkRect clipRect = {0, height / 2, width, height};
+ canvas.clipRect(clipRect);
+ }
+ {
+ gfx::SkiaBitLocker bitLocker(&canvas);
+ CGContextRef cgContext = bitLocker.cgContext();
+ CGColorRef testColor = CGColorGetConstantColor(kCGColorWhite);
+ CGContextSetFillColorWithColor(cgContext, testColor);
+ CGRect cgRect = {{0, 0}, {width, height}};
+ CGContextFillRect(cgContext, cgRect);
+ if (test & TestNoBits) {
+ if (test & TestClip) {
+ SkRect clipRect = {0, height / 2, width, height};
+ canvas.clipRect(clipRect);
+ }
+ }
+ }
+ const unsigned results[][storageSize] = {
+ {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF}, // identity
+ {0xFF333333, 0xFFFFFFFF, 0xFF999999, 0xFFFFFFFF}, // translate
+ {0xFF333333, 0xFF666666, 0xFFFFFFFF, 0xFFFFFFFF}, // clip
+ {0xFF333333, 0xFF666666, 0xFF999999, 0xFFFFFFFF} // translate | clip
+ };
+ for (unsigned index = 0; index < storageSize; index++)
+ EXPECT_EQ(results[test & ~TestNoBits][index], bits[index]);
+}
+
+void SkiaUtilsMacTest::ShapeHelper(int width, int height,
+ bool isred, bool tfbit) {
+ SkBitmap thing(CreateSkBitmap(width, height, isred, tfbit));
+
+ // Confirm size
+ NSImage* image = gfx::SkBitmapToNSImage(thing);
+ EXPECT_DOUBLE_EQ([image size].width, (double)width);
+ EXPECT_DOUBLE_EQ([image size].height, (double)height);
+
+ EXPECT_TRUE([[image representations] count] == 1);
+ EXPECT_TRUE([[[image representations] lastObject]
+ isKindOfClass:[NSBitmapImageRep class]]);
+ TestImageRep([[image representations] lastObject], isred);
+}
+
+TEST_F(SkiaUtilsMacTest, BitmapToNSImage_RedSquare64x64) {
+ ShapeHelper(64, 64, true, true);
+}
+
+TEST_F(SkiaUtilsMacTest, BitmapToNSImage_BlueRectangle199x19) {
+ ShapeHelper(199, 19, false, true);
+}
+
+TEST_F(SkiaUtilsMacTest, BitmapToNSImage_BlueRectangle444) {
+ ShapeHelper(200, 200, false, false);
+}
+
+TEST_F(SkiaUtilsMacTest, BitmapToNSBitmapImageRep_BlueRectangle20x30) {
+ int width = 20;
+ int height = 30;
+
+ SkBitmap bitmap(CreateSkBitmap(width, height, false, true));
+ NSBitmapImageRep* imageRep = gfx::SkBitmapToNSBitmapImageRep(bitmap);
+
+ EXPECT_DOUBLE_EQ(width, [imageRep size].width);
+ EXPECT_DOUBLE_EQ(height, [imageRep size].height);
+ TestImageRep(imageRep, false);
+}
+
+TEST_F(SkiaUtilsMacTest, NSImageRepToSkBitmap) {
+ int width = 10;
+ int height = 15;
+ bool isred = true;
+
+ NSImage* image = CreateNSImage(width, height, isred);
+ EXPECT_EQ(1u, [[image representations] count]);
+ NSBitmapImageRep* imageRep = [[image representations] lastObject];
+ SkBitmap bitmap(gfx::NSImageRepToSkBitmap(imageRep, [image size], false));
+ TestSkBitmap(bitmap, isred);
+}
+
+TEST_F(SkiaUtilsMacTest, BitLocker_Identity) {
+ RunBitLockerTest(SkiaUtilsMacTest::TestIdentity);
+}
+
+TEST_F(SkiaUtilsMacTest, BitLocker_Translate) {
+ RunBitLockerTest(SkiaUtilsMacTest::TestTranslate);
+}
+
+TEST_F(SkiaUtilsMacTest, BitLocker_Clip) {
+ RunBitLockerTest(SkiaUtilsMacTest::TestClip);
+}
+
+TEST_F(SkiaUtilsMacTest, BitLocker_XClip) {
+ RunBitLockerTest(SkiaUtilsMacTest::TestXClip);
+}
+
+TEST_F(SkiaUtilsMacTest, BitLocker_NoBits) {
+ RunBitLockerTest(SkiaUtilsMacTest::TestNoBits);
+}
+
+TEST_F(SkiaUtilsMacTest, BitLocker_TranslateNoBits) {
+ RunBitLockerTest(SkiaUtilsMacTest::TestTranslateNoBits);
+}
+
+TEST_F(SkiaUtilsMacTest, BitLocker_ClipNoBits) {
+ RunBitLockerTest(SkiaUtilsMacTest::TestClipNoBits);
+}
+
+TEST_F(SkiaUtilsMacTest, BitLocker_XClipNoBits) {
+ RunBitLockerTest(SkiaUtilsMacTest::TestXClipNoBits);
+}
+
+} // namespace
+
diff --git a/chromium/skia/ext/skia_utils_win.cc b/chromium/skia/ext/skia_utils_win.cc
new file mode 100644
index 00000000000..8b72b986b58
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_win.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/skia_utils_win.h"
+
+#include <windows.h>
+
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/effects/SkGradientShader.h"
+
+namespace {
+
+template <bool>
+struct CompileAssert {
+};
+
+#undef COMPILE_ASSERT
+#define COMPILE_ASSERT(expr, msg) \
+ typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
+
+COMPILE_ASSERT(SK_OFFSETOF(RECT, left) == SK_OFFSETOF(SkIRect, fLeft), o1);
+COMPILE_ASSERT(SK_OFFSETOF(RECT, top) == SK_OFFSETOF(SkIRect, fTop), o2);
+COMPILE_ASSERT(SK_OFFSETOF(RECT, right) == SK_OFFSETOF(SkIRect, fRight), o3);
+COMPILE_ASSERT(SK_OFFSETOF(RECT, bottom) == SK_OFFSETOF(SkIRect, fBottom), o4);
+COMPILE_ASSERT(sizeof(RECT().left) == sizeof(SkIRect().fLeft), o5);
+COMPILE_ASSERT(sizeof(RECT().top) == sizeof(SkIRect().fTop), o6);
+COMPILE_ASSERT(sizeof(RECT().right) == sizeof(SkIRect().fRight), o7);
+COMPILE_ASSERT(sizeof(RECT().bottom) == sizeof(SkIRect().fBottom), o8);
+COMPILE_ASSERT(sizeof(RECT) == sizeof(SkIRect), o9);
+
+} // namespace
+
+namespace skia {
+
+POINT SkPointToPOINT(const SkPoint& point) {
+ POINT win_point = { SkScalarRound(point.fX), SkScalarRound(point.fY) };
+ return win_point;
+}
+
+SkRect RECTToSkRect(const RECT& rect) {
+ SkRect sk_rect = { SkIntToScalar(rect.left), SkIntToScalar(rect.top),
+ SkIntToScalar(rect.right), SkIntToScalar(rect.bottom) };
+ return sk_rect;
+}
+
+SkColor COLORREFToSkColor(COLORREF color) {
+#ifndef _MSC_VER
+ return SkColorSetRGB(GetRValue(color), GetGValue(color), GetBValue(color));
+#else
+ // ARGB = 0xFF000000 | ((0BGR -> RGB0) >> 8)
+ return 0xFF000000u | (_byteswap_ulong(color) >> 8);
+#endif
+}
+
+COLORREF SkColorToCOLORREF(SkColor color) {
+#ifndef _MSC_VER
+ return RGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
+#else
+ // 0BGR = ((ARGB -> BGRA) >> 8)
+ return (_byteswap_ulong(color) >> 8);
+#endif
+}
+
+} // namespace skia
+
diff --git a/chromium/skia/ext/skia_utils_win.h b/chromium/skia/ext/skia_utils_win.h
new file mode 100644
index 00000000000..98e1b999be7
--- /dev/null
+++ b/chromium/skia/ext/skia_utils_win.h
@@ -0,0 +1,49 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_SKIA_UTILS_WIN_H_
+#define SKIA_EXT_SKIA_UTILS_WIN_H_
+
+#include "third_party/skia/include/core/SkColor.h"
+
+struct SkIRect;
+struct SkPoint;
+struct SkRect;
+typedef unsigned long DWORD;
+typedef DWORD COLORREF;
+typedef struct tagPOINT POINT;
+typedef struct tagRECT RECT;
+
+namespace skia {
+
+// Converts a Skia point to a Windows POINT.
+POINT SkPointToPOINT(const SkPoint& point);
+
+// Converts a Windows RECT to a Skia rect.
+SkRect RECTToSkRect(const RECT& rect);
+
+// Converts a Windows RECT to a Skia rect.
+// Both use same in-memory format. Verified by COMPILE_ASSERT() in
+// skia_utils.cc.
+inline const SkIRect& RECTToSkIRect(const RECT& rect) {
+ return reinterpret_cast<const SkIRect&>(rect);
+}
+
+// Converts a Skia rect to a Windows RECT.
+// Both use same in-memory format. Verified by COMPILE_ASSERT() in
+// skia_utils.cc.
+inline const RECT& SkIRectToRECT(const SkIRect& rect) {
+ return reinterpret_cast<const RECT&>(rect);
+}
+
+// Converts COLORREFs (0BGR) to the ARGB layout Skia expects.
+SK_API SkColor COLORREFToSkColor(COLORREF color);
+
+// Converts ARGB to COLORREFs (0BGR).
+SK_API COLORREF SkColorToCOLORREF(SkColor color);
+
+} // namespace skia
+
+#endif // SKIA_EXT_SKIA_UTILS_WIN_H_
+
diff --git a/chromium/skia/ext/vector_canvas.cc b/chromium/skia/ext/vector_canvas.cc
new file mode 100644
index 00000000000..9de6b3d0c59
--- /dev/null
+++ b/chromium/skia/ext/vector_canvas.cc
@@ -0,0 +1,38 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/vector_canvas.h"
+#include "third_party/skia/include/core/SkDevice.h"
+
+namespace skia {
+
+VectorCanvas::VectorCanvas(SkDevice* device)
+ : PlatformCanvas(device) {
+}
+
+VectorCanvas::~VectorCanvas() {
+}
+
+SkBounder* VectorCanvas::setBounder(SkBounder* bounder) {
+ if (!IsTopDeviceVectorial())
+ return PlatformCanvas::setBounder(bounder);
+
+ // This function isn't used in the code. Verify this assumption.
+ SkASSERT(false);
+ return NULL;
+}
+
+SkDrawFilter* VectorCanvas::setDrawFilter(SkDrawFilter* filter) {
+ // This function isn't used in the code. Verify this assumption.
+ SkASSERT(false);
+ return NULL;
+}
+
+bool VectorCanvas::IsTopDeviceVectorial() const {
+ SkDevice* device = GetTopDevice(*this);
+ return device->getDeviceCapabilities() & SkDevice::kVector_Capability;
+}
+
+} // namespace skia
+
diff --git a/chromium/skia/ext/vector_canvas.h b/chromium/skia/ext/vector_canvas.h
new file mode 100644
index 00000000000..e7a67fce9d2
--- /dev/null
+++ b/chromium/skia/ext/vector_canvas.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_VECTOR_CANVAS_H_
+#define SKIA_EXT_VECTOR_CANVAS_H_
+
+#include "base/compiler_specific.h"
+#include "skia/ext/platform_canvas.h"
+
+class SkDevice;
+
+namespace skia {
+
+// This class is a specialization of the regular PlatformCanvas. It is designed
+// to work with a VectorDevice to manage platform-specific drawing. It allows
+// using both Skia operations and platform-specific operations. It *doesn't*
+// support reading back from the bitmap backstore since it is not used.
+class SK_API VectorCanvas : public PlatformCanvas {
+ public:
+ // Ownership of |device| is transfered to VectorCanvas.
+ explicit VectorCanvas(SkDevice* device);
+ virtual ~VectorCanvas();
+
+ virtual SkBounder* setBounder(SkBounder* bounder) OVERRIDE;
+ virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter) OVERRIDE;
+
+ private:
+ // Returns true if the top device is vector based and not bitmap based.
+ bool IsTopDeviceVectorial() const;
+
+ // Copy & assign are not supported.
+ VectorCanvas(const VectorCanvas&);
+ const VectorCanvas& operator=(const VectorCanvas&);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_VECTOR_CANVAS_H_
+
diff --git a/chromium/skia/ext/vector_canvas_unittest.cc b/chromium/skia/ext/vector_canvas_unittest.cc
new file mode 100644
index 00000000000..a087aedf5d2
--- /dev/null
+++ b/chromium/skia/ext/vector_canvas_unittest.cc
@@ -0,0 +1,970 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "build/build_config.h"
+
+#if !defined(OS_WIN)
+#include <unistd.h>
+#endif
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "skia/ext/vector_canvas.h"
+#include "skia/ext/vector_platform_device_emf_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/effects/SkDashPathEffect.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/size.h"
+
+namespace skia {
+
+namespace {
+
+const char kGenerateSwitch[] = "vector-canvas-generate";
+
+// Lightweight HDC management.
+class Context {
+ public:
+ Context() : context_(CreateCompatibleDC(NULL)) {
+ EXPECT_TRUE(context_);
+ }
+ ~Context() {
+ DeleteDC(context_);
+ }
+
+ HDC context() const { return context_; }
+
+ private:
+ HDC context_;
+
+ DISALLOW_COPY_AND_ASSIGN(Context);
+};
+
+// Lightweight HBITMAP management.
+class Bitmap {
+ public:
+ Bitmap(const Context& context, int x, int y) {
+ BITMAPINFOHEADER hdr;
+ hdr.biSize = sizeof(BITMAPINFOHEADER);
+ hdr.biWidth = x;
+ hdr.biHeight = -y; // Minus means top-down bitmap.
+ hdr.biPlanes = 1;
+ hdr.biBitCount = 32;
+ hdr.biCompression = BI_RGB; // No compression.
+ hdr.biSizeImage = 0;
+ hdr.biXPelsPerMeter = 1;
+ hdr.biYPelsPerMeter = 1;
+ hdr.biClrUsed = 0;
+ hdr.biClrImportant = 0;
+ bitmap_ = CreateDIBSection(context.context(),
+ reinterpret_cast<BITMAPINFO*>(&hdr), 0,
+ &data_, NULL, 0);
+ EXPECT_TRUE(bitmap_);
+ EXPECT_TRUE(SelectObject(context.context(), bitmap_));
+ }
+ ~Bitmap() {
+ EXPECT_TRUE(DeleteObject(bitmap_));
+ }
+
+ private:
+ HBITMAP bitmap_;
+
+ void* data_;
+
+ DISALLOW_COPY_AND_ASSIGN(Bitmap);
+};
+
+// Lightweight raw-bitmap management. The image, once initialized, is immuable.
+// It is mainly used for comparison.
+class Image {
+ public:
+ // Creates the image from the given filename on disk.
+ explicit Image(const base::FilePath& filename) : ignore_alpha_(true) {
+ std::string compressed;
+ file_util::ReadFileToString(filename, &compressed);
+ EXPECT_TRUE(compressed.size());
+
+ SkBitmap bitmap;
+ EXPECT_TRUE(gfx::PNGCodec::Decode(
+ reinterpret_cast<const unsigned char*>(compressed.data()),
+ compressed.size(), &bitmap));
+ SetSkBitmap(bitmap);
+ }
+
+ // Loads the image from a canvas.
+ Image(skia::PlatformCanvas& canvas) : ignore_alpha_(true) {
+ // Use a different way to access the bitmap. The normal way would be to
+ // query the SkBitmap.
+ skia::ScopedPlatformPaint scoped_platform_paint(&canvas);
+ HDC context = scoped_platform_paint.GetPlatformSurface();
+ HGDIOBJ bitmap = GetCurrentObject(context, OBJ_BITMAP);
+ EXPECT_TRUE(bitmap != NULL);
+ // Initialize the clip region to the entire bitmap.
+ BITMAP bitmap_data;
+ EXPECT_EQ(GetObject(bitmap, sizeof(BITMAP), &bitmap_data), sizeof(BITMAP));
+ width_ = bitmap_data.bmWidth;
+ height_ = bitmap_data.bmHeight;
+ row_length_ = bitmap_data.bmWidthBytes;
+ size_t size = row_length_ * height_;
+ data_.resize(size);
+ memcpy(&*data_.begin(), bitmap_data.bmBits, size);
+ }
+
+ // Loads the image from a canvas.
+ Image(const SkBitmap& bitmap) : ignore_alpha_(true) {
+ SetSkBitmap(bitmap);
+ }
+
+ int width() const { return width_; }
+ int height() const { return height_; }
+ int row_length() const { return row_length_; }
+
+ // Save the image to a png file. Used to create the initial test files.
+ void SaveToFile(const base::FilePath& filename) {
+ std::vector<unsigned char> compressed;
+ ASSERT_TRUE(gfx::PNGCodec::Encode(&*data_.begin(),
+ gfx::PNGCodec::FORMAT_BGRA,
+ gfx::Size(width_, height_),
+ row_length_,
+ true,
+ std::vector<gfx::PNGCodec::Comment>(),
+ &compressed));
+ ASSERT_TRUE(compressed.size());
+ FILE* f = file_util::OpenFile(filename, "wb");
+ ASSERT_TRUE(f);
+ ASSERT_EQ(fwrite(&*compressed.begin(), 1, compressed.size(), f),
+ compressed.size());
+ file_util::CloseFile(f);
+ }
+
+ // Returns the percentage of the image that is different from the other,
+ // between 0 and 100.
+ double PercentageDifferent(const Image& rhs) const {
+ if (width_ != rhs.width_ ||
+ height_ != rhs.height_ ||
+ row_length_ != rhs.row_length_ ||
+ width_ == 0 ||
+ height_ == 0) {
+ return 100.; // When of different size or empty, they are 100% different.
+ }
+ // Compute pixels different in the overlap
+ int pixels_different = 0;
+ for (int y = 0; y < height_; ++y) {
+ for (int x = 0; x < width_; ++x) {
+ uint32_t lhs_pixel = pixel_at(x, y);
+ uint32_t rhs_pixel = rhs.pixel_at(x, y);
+ if (lhs_pixel != rhs_pixel)
+ ++pixels_different;
+ }
+ }
+
+ // Like the WebKit ImageDiff tool, we define percentage different in terms
+ // of the size of the 'actual' bitmap.
+ double total_pixels = static_cast<double>(width_) *
+ static_cast<double>(height_);
+ return static_cast<double>(pixels_different) / total_pixels * 100.;
+ }
+
+ // Returns the 0x0RGB or 0xARGB value of the pixel at the given location,
+ // depending on ignore_alpha_.
+ uint32 pixel_at(int x, int y) const {
+ EXPECT_TRUE(x >= 0 && x < width_);
+ EXPECT_TRUE(y >= 0 && y < height_);
+ const uint32* data = reinterpret_cast<const uint32*>(&*data_.begin());
+ const uint32* data_row = data + y * row_length_ / sizeof(uint32);
+ if (ignore_alpha_)
+ return data_row[x] & 0xFFFFFF; // Strip out A.
+ else
+ return data_row[x];
+ }
+
+ protected:
+ void SetSkBitmap(const SkBitmap& bitmap) {
+ SkAutoLockPixels lock(bitmap);
+ width_ = bitmap.width();
+ height_ = bitmap.height();
+ row_length_ = static_cast<int>(bitmap.rowBytes());
+ size_t size = row_length_ * height_;
+ data_.resize(size);
+ memcpy(&*data_.begin(), bitmap.getAddr(0, 0), size);
+ }
+
+ private:
+ // Pixel dimensions of the image.
+ int width_;
+ int height_;
+
+ // Length of a line in bytes.
+ int row_length_;
+
+ // Actual bitmap data in arrays of RGBAs (so when loaded as uint32, it's
+ // 0xABGR).
+ std::vector<unsigned char> data_;
+
+ // Flag to signal if the comparison functions should ignore the alpha channel.
+ const bool ignore_alpha_;
+
+ DISALLOW_COPY_AND_ASSIGN(Image);
+};
+
+// Base for tests. Capability to process an image.
+class ImageTest : public testing::Test {
+ public:
+ // In what state is the test running.
+ enum ProcessAction {
+ GENERATE,
+ COMPARE,
+ NOOP,
+ };
+
+ ImageTest(ProcessAction default_action)
+ : action_(default_action) {
+ }
+
+ protected:
+ virtual void SetUp() {
+ const testing::TestInfo& test_info =
+ *testing::UnitTest::GetInstance()->current_test_info();
+ PathService::Get(base::DIR_SOURCE_ROOT, &test_dir_);
+ test_dir_ = test_dir_.AppendASCII("skia").
+ AppendASCII("ext").
+ AppendASCII("data").
+ AppendASCII(test_info.test_case_name()).
+ AppendASCII(test_info.name());
+
+ // Hack for a quick lowercase. We assume all the tests names are ASCII.
+ base::FilePath::StringType tmp(test_dir_.value());
+ for (size_t i = 0; i < tmp.size(); ++i)
+ tmp[i] = base::ToLowerASCII(tmp[i]);
+ test_dir_ = base::FilePath(tmp);
+
+ if (action_ == GENERATE) {
+ // Make sure the directory exist.
+ file_util::CreateDirectory(test_dir_);
+ }
+ }
+
+ // Returns the fully qualified path of a data file.
+ base::FilePath test_file(const base::FilePath::StringType& filename) const {
+ // Hack for a quick lowercase. We assume all the test data file names are
+ // ASCII.
+#if defined(OS_WIN)
+ std::string tmp = WideToASCII(filename);
+#else
+ std::string tmp(filename);
+#endif
+ for (size_t i = 0; i < tmp.size(); ++i)
+ tmp[i] = base::ToLowerASCII(tmp[i]);
+
+ return test_dir_.AppendASCII(tmp);
+ }
+
+ // Compares or saves the bitmap currently loaded in the context, depending on
+ // kGenerating value. Returns 0 on success or any positive value between ]0,
+ // 100] on failure. The return value is the percentage of difference between
+ // the image in the file and the image in the canvas.
+ double ProcessCanvas(skia::PlatformCanvas& canvas,
+ base::FilePath::StringType filename) const {
+ filename = filename + FILE_PATH_LITERAL(".png");
+ switch (action_) {
+ case GENERATE:
+ SaveImage(canvas, filename);
+ return 0.;
+ case COMPARE:
+ return CompareImage(canvas, filename);
+ case NOOP:
+ return 0;
+ default:
+ // Invalid state, returns that the image is 100 different.
+ return 100.;
+ }
+ }
+
+ // Compares the bitmap currently loaded in the context with the file. Returns
+ // the percentage of pixel difference between both images, between 0 and 100.
+ double CompareImage(skia::PlatformCanvas& canvas,
+ const base::FilePath::StringType& filename) const {
+ Image image1(canvas);
+ Image image2(test_file(filename));
+ double diff = image1.PercentageDifferent(image2);
+ return diff;
+ }
+
+ // Saves the bitmap currently loaded in the context into the file.
+ void SaveImage(skia::PlatformCanvas& canvas,
+ const base::FilePath::StringType& filename) const {
+ Image(canvas).SaveToFile(test_file(filename));
+ }
+
+ ProcessAction action_;
+
+ // Path to directory used to contain the test data.
+ base::FilePath test_dir_;
+
+ DISALLOW_COPY_AND_ASSIGN(ImageTest);
+};
+
+// Premultiply the Alpha channel on the R, B and G channels.
+void Premultiply(SkBitmap bitmap) {
+ SkAutoLockPixels lock(bitmap);
+ for (int x = 0; x < bitmap.width(); ++x) {
+ for (int y = 0; y < bitmap.height(); ++y) {
+ uint32_t* pixel_addr = bitmap.getAddr32(x, y);
+ uint32_t color = *pixel_addr;
+ BYTE alpha = SkColorGetA(color);
+ if (!alpha) {
+ *pixel_addr = 0;
+ } else {
+ BYTE alpha_offset = alpha / 2;
+ *pixel_addr = SkColorSetARGB(
+ SkColorGetA(color),
+ (SkColorGetR(color) * 255 + alpha_offset) / alpha,
+ (SkColorGetG(color) * 255 + alpha_offset) / alpha,
+ (SkColorGetB(color) * 255 + alpha_offset) / alpha);
+ }
+ }
+ }
+}
+
+void LoadPngFileToSkBitmap(const base::FilePath& filename,
+ SkBitmap* bitmap,
+ bool is_opaque) {
+ std::string compressed;
+ file_util::ReadFileToString(base::MakeAbsoluteFilePath(filename),
+ &compressed);
+ ASSERT_TRUE(compressed.size());
+
+ ASSERT_TRUE(gfx::PNGCodec::Decode(
+ reinterpret_cast<const unsigned char*>(compressed.data()),
+ compressed.size(), bitmap));
+
+ EXPECT_EQ(is_opaque, bitmap->isOpaque());
+ Premultiply(*bitmap);
+}
+
+} // namespace
+
+// Streams an image.
+inline std::ostream& operator<<(std::ostream& out, const Image& image) {
+ return out << "Image(" << image.width() << ", "
+ << image.height() << ", " << image.row_length() << ")";
+}
+
+// Runs simultaneously the same drawing commands on VectorCanvas and
+// PlatformCanvas and compare the results.
+class VectorCanvasTest : public ImageTest {
+ public:
+ typedef ImageTest parent;
+
+ VectorCanvasTest() : parent(CurrentMode()), compare_canvas_(true) {
+ }
+
+ protected:
+ virtual void SetUp() {
+ parent::SetUp();
+ Init(100);
+ number_ = 0;
+ }
+
+ virtual void TearDown() {
+ delete pcanvas_;
+ pcanvas_ = NULL;
+
+ delete vcanvas_;
+ vcanvas_ = NULL;
+
+ delete bitmap_;
+ bitmap_ = NULL;
+
+ delete context_;
+ context_ = NULL;
+
+ parent::TearDown();
+ }
+
+ void Init(int size) {
+ size_ = size;
+ context_ = new Context();
+ bitmap_ = new Bitmap(*context_, size_, size_);
+ vcanvas_ = new VectorCanvas(
+ VectorPlatformDeviceEmf::CreateDevice(
+ size_, size_, true, context_->context()));
+ pcanvas_ = CreatePlatformCanvas(size_, size_, false);
+
+ // Clear white.
+ vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
+ pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
+ }
+
+ // Compares both canvas and returns the pixel difference in percentage between
+ // both images. 0 on success and ]0, 100] on failure.
+ double ProcessImage(const base::FilePath::StringType& filename) {
+ std::wstring number(base::StringPrintf(L"%02d_", number_++));
+ double diff1 = parent::ProcessCanvas(*vcanvas_, number + L"vc_" + filename);
+ double diff2 = parent::ProcessCanvas(*pcanvas_, number + L"pc_" + filename);
+ if (!compare_canvas_)
+ return std::max(diff1, diff2);
+
+ Image image1(*vcanvas_);
+ Image image2(*pcanvas_);
+ double diff = image1.PercentageDifferent(image2);
+ return std::max(std::max(diff1, diff2), diff);
+ }
+
+ // Returns COMPARE, which is the default. If kGenerateSwitch command
+ // line argument is used to start this process, GENERATE is returned instead.
+ static ProcessAction CurrentMode() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(kGenerateSwitch) ?
+ GENERATE : COMPARE;
+ }
+
+ // Length in x and y of the square canvas.
+ int size_;
+
+ // Current image number in the current test. Used to number of test files.
+ int number_;
+
+ // A temporary HDC to draw into.
+ Context* context_;
+
+ // Bitmap created inside context_.
+ Bitmap* bitmap_;
+
+ // Vector based canvas.
+ VectorCanvas* vcanvas_;
+
+ // Pixel based canvas.
+ PlatformCanvas* pcanvas_;
+
+ // When true (default), vcanvas_ and pcanvas_ contents are compared and
+ // verified to be identical.
+ bool compare_canvas_;
+};
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Actual tests
+
+#if !defined(USE_AURA) // http://crbug.com/154358
+
+TEST_F(VectorCanvasTest, BasicDrawing) {
+ EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.)
+ << L"clean";
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clean")));
+
+ // Clear white.
+ {
+ vcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
+ pcanvas_->drawARGB(255, 255, 255, 255, SkXfermode::kSrc_Mode);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawARGB")));
+
+ // Diagonal line top-left to bottom-right.
+ {
+ SkPaint paint;
+ // Default color is black.
+ vcanvas_->drawLine(10, 10, 90, 90, paint);
+ pcanvas_->drawLine(10, 10, 90, 90, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_black")));
+
+ // Rect.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorGREEN);
+ vcanvas_->drawRectCoords(25, 25, 75, 75, paint);
+ pcanvas_->drawRectCoords(25, 25, 75, 75, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_green")));
+
+ // A single-point rect doesn't leave any mark.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+ vcanvas_->drawRectCoords(5, 5, 5, 5, paint);
+ pcanvas_->drawRectCoords(5, 5, 5, 5, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
+
+ // Rect.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorBLUE);
+ vcanvas_->drawRectCoords(75, 50, 80, 55, paint);
+ pcanvas_->drawRectCoords(75, 50, 80, 55, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawRect_noop")));
+
+ // Empty again
+ {
+ vcanvas_->drawPaint(SkPaint());
+ pcanvas_->drawPaint(SkPaint());
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPaint_black")));
+
+ // Horizontal line left to right.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawLine(10, 20, 90, 20, paint);
+ pcanvas_->drawLine(10, 20, 90, 20, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_left_to_right")));
+
+ // Vertical line downward.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawLine(30, 10, 30, 90, paint);
+ pcanvas_->drawLine(30, 10, 30, 90, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawLine_red")));
+}
+
+TEST_F(VectorCanvasTest, Circles) {
+ // There is NO WAY to make them agree. At least verify that the output doesn't
+ // change across versions. This test is disabled. See bug 1060231.
+ compare_canvas_ = false;
+
+ // Stroked Circle.
+ {
+ SkPaint paint;
+ SkPath path;
+ path.addCircle(50, 75, 10);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorMAGENTA);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke")));
+
+ // Filled Circle.
+ {
+ SkPaint paint;
+ SkPath path;
+ path.addCircle(50, 25, 10);
+ paint.setStyle(SkPaint::kFill_Style);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_fill")));
+
+ // Stroked Circle over.
+ {
+ SkPaint paint;
+ SkPath path;
+ path.addCircle(50, 25, 10);
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorBLUE);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_over_strike")));
+
+ // Stroke and Fill Circle.
+ {
+ SkPaint paint;
+ SkPath path;
+ path.addCircle(12, 50, 10);
+ paint.setStyle(SkPaint::kStrokeAndFill_Style);
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle_stroke_and_fill")));
+
+ // Line + Quad + Cubic.
+ {
+ SkPaint paint;
+ SkPath path;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorGREEN);
+ path.moveTo(1, 1);
+ path.lineTo(60, 40);
+ path.lineTo(80, 80);
+ path.quadTo(20, 50, 10, 90);
+ path.quadTo(50, 20, 90, 10);
+ path.cubicTo(20, 40, 50, 50, 10, 10);
+ path.cubicTo(30, 20, 50, 50, 90, 10);
+ path.addRect(90, 90, 95, 96);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("mixed_stroke")));
+}
+
+TEST_F(VectorCanvasTest, LineOrientation) {
+ // There is NO WAY to make them agree. At least verify that the output doesn't
+ // change across versions. This test is disabled. See bug 1060231.
+ compare_canvas_ = false;
+
+ // Horizontal lines.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ // Left to right.
+ vcanvas_->drawLine(10, 20, 90, 20, paint);
+ pcanvas_->drawLine(10, 20, 90, 20, paint);
+ // Right to left.
+ vcanvas_->drawLine(90, 30, 10, 30, paint);
+ pcanvas_->drawLine(90, 30, 10, 30, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal")));
+
+ // Vertical lines.
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ // Top down.
+ vcanvas_->drawLine(20, 10, 20, 90, paint);
+ pcanvas_->drawLine(20, 10, 20, 90, paint);
+ // Bottom up.
+ vcanvas_->drawLine(30, 90, 30, 10, paint);
+ pcanvas_->drawLine(30, 90, 30, 10, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical")));
+
+ // Try again with a 180 degres rotation.
+ vcanvas_->rotate(180);
+ pcanvas_->rotate(180);
+
+ // Horizontal lines (rotated).
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawLine(-10, -25, -90, -25, paint);
+ pcanvas_->drawLine(-10, -25, -90, -25, paint);
+ vcanvas_->drawLine(-90, -35, -10, -35, paint);
+ pcanvas_->drawLine(-90, -35, -10, -35, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("horizontal_180")));
+
+ // Vertical lines (rotated).
+ {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+ vcanvas_->drawLine(-25, -10, -25, -90, paint);
+ pcanvas_->drawLine(-25, -10, -25, -90, paint);
+ vcanvas_->drawLine(-35, -90, -35, -10, paint);
+ pcanvas_->drawLine(-35, -90, -35, -10, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("vertical_180")));
+}
+
+TEST_F(VectorCanvasTest, PathOrientation) {
+ // There is NO WAY to make them agree. At least verify that the output doesn't
+ // change across versions. This test is disabled. See bug 1060231.
+ compare_canvas_ = false;
+
+ // Horizontal lines.
+ {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorRED);
+ SkPath path;
+ SkPoint start;
+ start.set(10, 20);
+ SkPoint end;
+ end.set(90, 20);
+ path.moveTo(start);
+ path.lineTo(end);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_ltr")));
+
+ // Horizontal lines.
+ {
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setColor(SK_ColorRED);
+ SkPath path;
+ SkPoint start;
+ start.set(90, 30);
+ SkPoint end;
+ end.set(10, 30);
+ path.moveTo(start);
+ path.lineTo(end);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("drawPath_rtl")));
+}
+
+TEST_F(VectorCanvasTest, DiagonalLines) {
+ SkPaint paint;
+ paint.setColor(SK_ColorRED);
+
+ vcanvas_->drawLine(10, 10, 90, 90, paint);
+ pcanvas_->drawLine(10, 10, 90, 90, paint);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("nw-se")));
+
+ // Starting here, there is NO WAY to make them agree. At least verify that the
+ // output doesn't change across versions. This test is disabled. See bug
+ // 1060231.
+ compare_canvas_ = false;
+
+ vcanvas_->drawLine(10, 95, 90, 15, paint);
+ pcanvas_->drawLine(10, 95, 90, 15, paint);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("sw-ne")));
+
+ vcanvas_->drawLine(90, 10, 10, 90, paint);
+ pcanvas_->drawLine(90, 10, 10, 90, paint);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("ne-sw")));
+
+ vcanvas_->drawLine(95, 90, 15, 10, paint);
+ pcanvas_->drawLine(95, 90, 15, 10, paint);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("se-nw")));
+}
+
+#if defined(OS_WIN)
+#define MAYBE_PathEffects DISABLED_PathEffects
+#else
+#define MAYBE_PathEffects PathEffects
+#endif
+TEST_F(VectorCanvasTest, MAYBE_PathEffects) {
+ {
+ SkPaint paint;
+ SkScalar intervals[] = { 1, 1 };
+ skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
+ new SkDashPathEffect(intervals, arraysize(intervals), 0));
+ paint.setPathEffect(effect.get());
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ vcanvas_->drawLine(10, 10, 90, 10, paint);
+ pcanvas_->drawLine(10, 10, 90, 10, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_line")));
+
+
+ // Starting here, there is NO WAY to make them agree. At least verify that the
+ // output doesn't change across versions. This test is disabled. See bug
+ // 1060231.
+ compare_canvas_ = false;
+
+ {
+ SkPaint paint;
+ SkScalar intervals[] = { 3, 5 };
+ skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
+ new SkDashPathEffect(intervals, arraysize(intervals), 0));
+ paint.setPathEffect(effect.get());
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ path.moveTo(10, 15);
+ path.lineTo(90, 15);
+ path.lineTo(90, 90);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_path")));
+
+ {
+ SkPaint paint;
+ SkScalar intervals[] = { 2, 1 };
+ skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
+ new SkDashPathEffect(intervals, arraysize(intervals), 0));
+ paint.setPathEffect(effect.get());
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ vcanvas_->drawRectCoords(20, 20, 30, 30, paint);
+ pcanvas_->drawRectCoords(20, 20, 30, 30, paint);
+ }
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("dash_rect")));
+
+ // This thing looks like it has been drawn by a 3 years old kid. I haven't
+ // filed a bug on this since I guess nobody is expecting this to look nice.
+ {
+ SkPaint paint;
+ SkScalar intervals[] = { 1, 1 };
+ skia::RefPtr<SkPathEffect> effect = skia::AdoptRef(
+ new SkDashPathEffect(intervals, arraysize(intervals), 0));
+ paint.setPathEffect(effect.get());
+ paint.setColor(SK_ColorMAGENTA);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ SkPath path;
+ path.addCircle(50, 75, 10);
+ vcanvas_->drawPath(path, paint);
+ pcanvas_->drawPath(path, paint);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("circle")));
+ }
+}
+
+TEST_F(VectorCanvasTest, Bitmaps) {
+ {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap, true);
+ vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("opaque")));
+ }
+
+ {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap, false);
+ vcanvas_->drawBitmap(bitmap, 5, 15, NULL);
+ pcanvas_->drawBitmap(bitmap, 5, 15, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("alpha")));
+ }
+}
+
+TEST_F(VectorCanvasTest, ClippingRect) {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
+ true);
+ SkRect rect;
+ rect.fLeft = 2;
+ rect.fTop = 2;
+ rect.fRight = 30.5f;
+ rect.fBottom = 30.5f;
+ vcanvas_->clipRect(rect);
+ pcanvas_->clipRect(rect);
+
+ vcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 13, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rect")));
+}
+
+TEST_F(VectorCanvasTest, ClippingPath) {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
+ true);
+ SkPath path;
+ path.addCircle(20, 20, 10);
+ vcanvas_->clipPath(path);
+ pcanvas_->clipPath(path);
+
+ vcanvas_->drawBitmap(bitmap, 14, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 14, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("path")));
+}
+
+TEST_F(VectorCanvasTest, ClippingCombined) {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
+ true);
+
+ SkRect rect;
+ rect.fLeft = 2;
+ rect.fTop = 2;
+ rect.fRight = 30.5f;
+ rect.fBottom = 30.5f;
+ vcanvas_->clipRect(rect);
+ pcanvas_->clipRect(rect);
+ SkPath path;
+ path.addCircle(20, 20, 10);
+ vcanvas_->clipPath(path, SkRegion::kUnion_Op);
+ pcanvas_->clipPath(path, SkRegion::kUnion_Op);
+
+ vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("combined")));
+}
+
+TEST_F(VectorCanvasTest, ClippingIntersect) {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
+ true);
+
+ SkRect rect;
+ rect.fLeft = 2;
+ rect.fTop = 2;
+ rect.fRight = 30.5f;
+ rect.fBottom = 30.5f;
+ vcanvas_->clipRect(rect);
+ pcanvas_->clipRect(rect);
+ SkPath path;
+ path.addCircle(23, 23, 15);
+ vcanvas_->clipPath(path);
+ pcanvas_->clipPath(path);
+
+ vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("intersect")));
+}
+
+TEST_F(VectorCanvasTest, ClippingClean) {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
+ true);
+ {
+ SkAutoCanvasRestore acrv(vcanvas_, true);
+ SkAutoCanvasRestore acrp(pcanvas_, true);
+ SkRect rect;
+ rect.fLeft = 2;
+ rect.fTop = 2;
+ rect.fRight = 30.5f;
+ rect.fBottom = 30.5f;
+ vcanvas_->clipRect(rect);
+ pcanvas_->clipRect(rect);
+
+ vcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 15, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("clipped")));
+ }
+ {
+ // Verify that the clipping region has been fixed back.
+ vcanvas_->drawBitmap(bitmap, 55, 3, NULL);
+ pcanvas_->drawBitmap(bitmap, 55, 3, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("unclipped")));
+ }
+}
+
+// See http://crbug.com/26938
+TEST_F(VectorCanvasTest, DISABLED_Matrix) {
+ SkBitmap bitmap;
+ LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap,
+ true);
+ {
+ vcanvas_->translate(15, 3);
+ pcanvas_->translate(15, 3);
+ vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+ pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate1")));
+ }
+ {
+ vcanvas_->translate(-30, -23);
+ pcanvas_->translate(-30, -23);
+ vcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+ pcanvas_->drawBitmap(bitmap, 0, 0, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("translate2")));
+ }
+ vcanvas_->resetMatrix();
+ pcanvas_->resetMatrix();
+
+ // For scaling and rotation, they use a different algorithm (nearest
+ // neighborhood vs smoothing). At least verify that the output doesn't change
+ // across versions.
+ compare_canvas_ = false;
+
+ {
+ vcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
+ pcanvas_->scale(SkDoubleToScalar(1.9), SkDoubleToScalar(1.5));
+ vcanvas_->drawBitmap(bitmap, 1, 1, NULL);
+ pcanvas_->drawBitmap(bitmap, 1, 1, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("scale")));
+ }
+ vcanvas_->resetMatrix();
+ pcanvas_->resetMatrix();
+
+ {
+ vcanvas_->rotate(67);
+ pcanvas_->rotate(67);
+ vcanvas_->drawBitmap(bitmap, 20, -50, NULL);
+ pcanvas_->drawBitmap(bitmap, 20, -50, NULL);
+ EXPECT_EQ(0., ProcessImage(FILE_PATH_LITERAL("rotate")));
+ }
+}
+
+#endif // !defined(USE_AURA)
+
+} // namespace skia
diff --git a/chromium/skia/ext/vector_platform_device_emf_win.cc b/chromium/skia/ext/vector_platform_device_emf_win.cc
new file mode 100644
index 00000000000..958ff8f4094
--- /dev/null
+++ b/chromium/skia/ext/vector_platform_device_emf_win.cc
@@ -0,0 +1,986 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/vector_platform_device_emf_win.h"
+
+#include <windows.h>
+
+#include "base/logging.h"
+#include "base/strings/string16.h"
+#include "skia/ext/bitmap_platform_device.h"
+#include "skia/ext/skia_utils_win.h"
+#include "third_party/skia/include/core/SkFontHost.h"
+#include "third_party/skia/include/core/SkPathEffect.h"
+#include "third_party/skia/include/core/SkTemplates.h"
+#include "third_party/skia/include/core/SkUtils.h"
+#include "third_party/skia/include/ports/SkTypeface_win.h"
+
+namespace skia {
+
+#define CHECK_FOR_NODRAW_ANNOTATION(paint) \
+ do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
+
+// static
+SkDevice* VectorPlatformDeviceEmf::CreateDevice(
+ int width, int height, bool is_opaque, HANDLE shared_section) {
+ if (!is_opaque) {
+ // TODO(maruel): http://crbug.com/18382 When restoring a semi-transparent
+ // layer, i.e. merging it, we need to rasterize it because GDI doesn't
+ // support transparency except for AlphaBlend(). Right now, a
+ // BitmapPlatformDevice is created when VectorCanvas think a saveLayers()
+ // call is being done. The way to save a layer would be to create an
+ // EMF-based VectorDevice and have this device registers the drawing. When
+ // playing back the device into a bitmap, do it at the printer's dpi instead
+ // of the layout's dpi (which is much lower).
+ return BitmapPlatformDevice::Create(width, height, is_opaque,
+ shared_section);
+ }
+
+ // TODO(maruel): http://crbug.com/18383 Look if it would be worth to
+ // increase the resolution by ~10x (any worthy factor) to increase the
+ // rendering precision (think about printing) while using a relatively
+ // low dpi. This happens because we receive float as input but the GDI
+ // functions works with integers. The idea is to premultiply the matrix
+ // with this factor and multiply each SkScalar that are passed to
+ // SkScalarRound(value) as SkScalarRound(value * 10). Safari is already
+ // doing the same for text rendering.
+ SkASSERT(shared_section);
+ SkDevice* device = VectorPlatformDeviceEmf::create(
+ reinterpret_cast<HDC>(shared_section), width, height);
+ return device;
+}
+
+static void FillBitmapInfoHeader(int width, int height, BITMAPINFOHEADER* hdr) {
+ hdr->biSize = sizeof(BITMAPINFOHEADER);
+ hdr->biWidth = width;
+ hdr->biHeight = -height; // Minus means top-down bitmap.
+ hdr->biPlanes = 1;
+ hdr->biBitCount = 32;
+ hdr->biCompression = BI_RGB; // no compression
+ hdr->biSizeImage = 0;
+ hdr->biXPelsPerMeter = 1;
+ hdr->biYPelsPerMeter = 1;
+ hdr->biClrUsed = 0;
+ hdr->biClrImportant = 0;
+}
+
+SkDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) {
+ InitializeDC(dc);
+
+ // Link the SkBitmap to the current selected bitmap in the device context.
+ SkBitmap bitmap;
+ HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP);
+ bool succeeded = false;
+ if (selected_bitmap != NULL) {
+ BITMAP bitmap_data;
+ if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) ==
+ sizeof(BITMAP)) {
+ // The context has a bitmap attached. Attach our SkBitmap to it.
+ // Warning: If the bitmap gets unselected from the HDC,
+ // VectorPlatformDeviceEmf has no way to detect this, so the HBITMAP
+ // could be released while SkBitmap still has a reference to it. Be
+ // cautious.
+ if (width == bitmap_data.bmWidth &&
+ height == bitmap_data.bmHeight) {
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config,
+ bitmap_data.bmWidth,
+ bitmap_data.bmHeight,
+ bitmap_data.bmWidthBytes);
+ bitmap.setPixels(bitmap_data.bmBits);
+ succeeded = true;
+ }
+ }
+ }
+
+ if (!succeeded)
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+
+ return new VectorPlatformDeviceEmf(dc, bitmap);
+}
+
+VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap)
+ : SkDevice(bitmap),
+ hdc_(dc),
+ previous_brush_(NULL),
+ previous_pen_(NULL) {
+ transform_.reset();
+ SetPlatformDevice(this, this);
+}
+
+VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() {
+ SkASSERT(previous_brush_ == NULL);
+ SkASSERT(previous_pen_ == NULL);
+}
+
+HDC VectorPlatformDeviceEmf::BeginPlatformPaint() {
+ return hdc_;
+}
+
+uint32_t VectorPlatformDeviceEmf::getDeviceCapabilities() {
+ return SkDevice::getDeviceCapabilities() | kVector_Capability;
+}
+
+void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw,
+ const SkPaint& paint) {
+ // TODO(maruel): Bypass the current transformation matrix.
+ SkRect rect;
+ rect.fLeft = 0;
+ rect.fTop = 0;
+ rect.fRight = SkIntToScalar(width() + 1);
+ rect.fBottom = SkIntToScalar(height() + 1);
+ drawRect(draw, rect, paint);
+}
+
+void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw,
+ SkCanvas::PointMode mode,
+ size_t count,
+ const SkPoint pts[],
+ const SkPaint& paint) {
+ if (!count)
+ return;
+
+ if (mode == SkCanvas::kPoints_PointMode) {
+ SkASSERT(false);
+ return;
+ }
+
+ SkPaint tmp_paint(paint);
+ tmp_paint.setStyle(SkPaint::kStroke_Style);
+
+ // Draw a path instead.
+ SkPath path;
+ switch (mode) {
+ case SkCanvas::kLines_PointMode:
+ if (count % 2) {
+ SkASSERT(false);
+ return;
+ }
+ for (size_t i = 0; i < count / 2; ++i) {
+ path.moveTo(pts[2 * i]);
+ path.lineTo(pts[2 * i + 1]);
+ }
+ break;
+ case SkCanvas::kPolygon_PointMode:
+ path.moveTo(pts[0]);
+ for (size_t i = 1; i < count; ++i) {
+ path.lineTo(pts[i]);
+ }
+ break;
+ default:
+ SkASSERT(false);
+ return;
+ }
+ // Draw the calculated path.
+ drawPath(draw, path, tmp_paint);
+}
+
+void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw,
+ const SkRect& rect,
+ const SkPaint& paint) {
+ CHECK_FOR_NODRAW_ANNOTATION(paint);
+ if (paint.getPathEffect()) {
+ // Draw a path instead.
+ SkPath path_orginal;
+ path_orginal.addRect(rect);
+
+ // Apply the path effect to the rect.
+ SkPath path_modified;
+ paint.getFillPath(path_orginal, &path_modified);
+
+ // Removes the path effect from the temporary SkPaint object.
+ SkPaint paint_no_effet(paint);
+ paint_no_effet.setPathEffect(NULL);
+
+ // Draw the calculated path.
+ drawPath(draw, path_modified, paint_no_effet);
+ return;
+ }
+
+ if (!ApplyPaint(paint)) {
+ return;
+ }
+ HDC dc = BeginPlatformPaint();
+ if (!Rectangle(dc, SkScalarRound(rect.fLeft),
+ SkScalarRound(rect.fTop),
+ SkScalarRound(rect.fRight),
+ SkScalarRound(rect.fBottom))) {
+ SkASSERT(false);
+ }
+ EndPlatformPaint();
+ Cleanup();
+}
+
+void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw,
+ const SkPath& path,
+ const SkPaint& paint,
+ const SkMatrix* prePathMatrix,
+ bool pathIsMutable) {
+ CHECK_FOR_NODRAW_ANNOTATION(paint);
+ if (paint.getPathEffect()) {
+ // Apply the path effect forehand.
+ SkPath path_modified;
+ paint.getFillPath(path, &path_modified);
+
+ // Removes the path effect from the temporary SkPaint object.
+ SkPaint paint_no_effet(paint);
+ paint_no_effet.setPathEffect(NULL);
+
+ // Draw the calculated path.
+ drawPath(draw, path_modified, paint_no_effet);
+ return;
+ }
+
+ if (!ApplyPaint(paint)) {
+ return;
+ }
+ HDC dc = BeginPlatformPaint();
+ if (PlatformDevice::LoadPathToDC(dc, path)) {
+ switch (paint.getStyle()) {
+ case SkPaint::kFill_Style: {
+ BOOL res = StrokeAndFillPath(dc);
+ SkASSERT(res != 0);
+ break;
+ }
+ case SkPaint::kStroke_Style: {
+ BOOL res = StrokePath(dc);
+ SkASSERT(res != 0);
+ break;
+ }
+ case SkPaint::kStrokeAndFill_Style: {
+ BOOL res = StrokeAndFillPath(dc);
+ SkASSERT(res != 0);
+ break;
+ }
+ default:
+ SkASSERT(false);
+ break;
+ }
+ }
+ EndPlatformPaint();
+ Cleanup();
+}
+
+void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkRect* src,
+ const SkRect& dst,
+ const SkPaint& paint) {
+ SkMatrix matrix;
+ SkRect bitmapBounds, tmpSrc, tmpDst;
+ SkBitmap tmpBitmap;
+
+ bitmapBounds.isetWH(bitmap.width(), bitmap.height());
+
+ // Compute matrix from the two rectangles
+ if (src) {
+ tmpSrc = *src;
+ } else {
+ tmpSrc = bitmapBounds;
+ }
+ matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+
+ const SkBitmap* bitmapPtr = &bitmap;
+
+ // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
+ // needed (if the src was clipped). No check needed if src==null.
+ if (src) {
+ if (!bitmapBounds.contains(*src)) {
+ if (!tmpSrc.intersect(bitmapBounds)) {
+ return; // nothing to draw
+ }
+ // recompute dst, based on the smaller tmpSrc
+ matrix.mapRect(&tmpDst, tmpSrc);
+ }
+
+ // since we may need to clamp to the borders of the src rect within
+ // the bitmap, we extract a subset.
+ // TODO: make sure this is handled in drawrect and remove it from here.
+ SkIRect srcIR;
+ tmpSrc.roundOut(&srcIR);
+ if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
+ return;
+ }
+ bitmapPtr = &tmpBitmap;
+
+ // Since we did an extract, we need to adjust the matrix accordingly
+ SkScalar dx = 0, dy = 0;
+ if (srcIR.fLeft > 0) {
+ dx = SkIntToScalar(srcIR.fLeft);
+ }
+ if (srcIR.fTop > 0) {
+ dy = SkIntToScalar(srcIR.fTop);
+ }
+ if (dx || dy) {
+ matrix.preTranslate(dx, dy);
+ }
+ }
+ this->drawBitmap(draw, *bitmapPtr, matrix, paint);
+}
+
+void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkMatrix& matrix,
+ const SkPaint& paint) {
+ // Load the temporary matrix. This is what will translate, rotate and resize
+ // the bitmap.
+ SkMatrix actual_transform(transform_);
+ actual_transform.preConcat(matrix);
+ LoadTransformToDC(hdc_, actual_transform);
+
+ InternalDrawBitmap(bitmap, 0, 0, paint);
+
+ // Restore the original matrix.
+ LoadTransformToDC(hdc_, transform_);
+}
+
+void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ int x, int y,
+ const SkPaint& paint) {
+ SkMatrix identity;
+ identity.reset();
+ LoadTransformToDC(hdc_, identity);
+
+ InternalDrawBitmap(bitmap, x, y, paint);
+
+ // Restore the original matrix.
+ LoadTransformToDC(hdc_, transform_);
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+static bool gdiCanHandleText(const SkPaint& paint) {
+ return !paint.getShader() &&
+ !paint.getPathEffect() &&
+ (SkPaint::kFill_Style == paint.getStyle()) &&
+ (255 == paint.getAlpha());
+}
+
+class SkGDIFontSetup {
+ public:
+ SkGDIFontSetup() :
+ fHDC(NULL),
+ fNewFont(NULL),
+ fSavedFont(NULL),
+ fSavedTextColor(0),
+ fUseGDI(false) {
+ SkDEBUGCODE(fUseGDIHasBeenCalled = false;)
+ }
+ ~SkGDIFontSetup();
+
+ // can only be called once
+ bool useGDI(HDC hdc, const SkPaint&);
+
+ private:
+ HDC fHDC;
+ HFONT fNewFont;
+ HFONT fSavedFont;
+ COLORREF fSavedTextColor;
+ bool fUseGDI;
+ SkDEBUGCODE(bool fUseGDIHasBeenCalled;)
+};
+
+bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) {
+ SkASSERT(!fUseGDIHasBeenCalled);
+ SkDEBUGCODE(fUseGDIHasBeenCalled = true;)
+
+ fUseGDI = gdiCanHandleText(paint);
+ if (fUseGDI) {
+ fSavedTextColor = GetTextColor(hdc);
+ SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor()));
+
+ LOGFONT lf;
+ SkLOGFONTFromTypeface(paint.getTypeface(), &lf);
+ lf.lfHeight = -SkScalarRound(paint.getTextSize());
+ fNewFont = CreateFontIndirect(&lf);
+ fSavedFont = (HFONT)::SelectObject(hdc, fNewFont);
+ fHDC = hdc;
+ }
+ return fUseGDI;
+}
+
+SkGDIFontSetup::~SkGDIFontSetup() {
+ if (fUseGDI) {
+ ::SelectObject(fHDC, fSavedFont);
+ ::DeleteObject(fNewFont);
+ SetTextColor(fHDC, fSavedTextColor);
+ }
+}
+
+static SkScalar getAscent(const SkPaint& paint) {
+ SkPaint::FontMetrics fm;
+ paint.getFontMetrics(&fm);
+ return fm.fAscent;
+}
+
+// return the options int for ExtTextOut. Only valid if the paint's text
+// encoding is not UTF8 (in which case ExtTextOut can't be used).
+static UINT getTextOutOptions(const SkPaint& paint) {
+ if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) {
+ return ETO_GLYPH_INDEX;
+ } else {
+ SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding());
+ return 0;
+ }
+}
+
+static SkiaEnsureTypefaceCharactersAccessible
+ g_skia_ensure_typeface_characters_accessible = NULL;
+
+SK_API void SetSkiaEnsureTypefaceCharactersAccessible(
+ SkiaEnsureTypefaceCharactersAccessible func) {
+ // This function is supposed to be called once in process life time.
+ SkASSERT(g_skia_ensure_typeface_characters_accessible == NULL);
+ g_skia_ensure_typeface_characters_accessible = func;
+}
+
+void EnsureTypefaceCharactersAccessible(
+ const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) {
+ LOGFONT lf;
+ SkLOGFONTFromTypeface(&typeface, &lf);
+ g_skia_ensure_typeface_characters_accessible(lf, text, text_length);
+}
+
+bool EnsureExtTextOut(HDC hdc, int x, int y, UINT options, const RECT * lprect,
+ LPCWSTR text, unsigned int characters, const int * lpDx,
+ SkTypeface* const typeface) {
+ bool success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
+ if (!success) {
+ if (typeface) {
+ EnsureTypefaceCharactersAccessible(*typeface,
+ text,
+ characters);
+ success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx);
+ if (!success) {
+ LOGFONT lf;
+ SkLOGFONTFromTypeface(typeface, &lf);
+ VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for "
+ << " FaceName = " << lf.lfFaceName
+ << " and characters: " << string16(text, characters);
+ }
+ } else {
+ VLOG(1) << "ExtTextOut FAILED for default FaceName "
+ << " and characters: " << string16(text, characters);
+ }
+ }
+ return success;
+}
+
+void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
+ const void* text,
+ size_t byteLength,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) {
+ SkGDIFontSetup setup;
+ bool useDrawPath = true;
+
+ if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
+ && setup.useGDI(hdc_, paint)) {
+ UINT options = getTextOutOptions(paint);
+ UINT count = byteLength >> 1;
+ useDrawPath = !EnsureExtTextOut(hdc_, SkScalarRound(x),
+ SkScalarRound(y + getAscent(paint)), options, 0,
+ reinterpret_cast<const wchar_t*>(text), count, NULL,
+ paint.getTypeface());
+ }
+
+ if (useDrawPath) {
+ SkPath path;
+ paint.getTextPath(text, byteLength, x, y, &path);
+ drawPath(draw, path, paint);
+ }
+}
+
+static size_t size_utf8(const char* text) {
+ return SkUTF8_CountUTF8Bytes(text);
+}
+
+static size_t size_utf16(const char* text) {
+ uint16_t c = *reinterpret_cast<const uint16_t*>(text);
+ return SkUTF16_IsHighSurrogate(c) ? 4 : 2;
+}
+
+static size_t size_glyphid(const char* text) {
+ return 2;
+}
+
+void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkScalar pos[],
+ SkScalar constY,
+ int scalarsPerPos,
+ const SkPaint& paint) {
+ SkGDIFontSetup setup;
+ bool useDrawText = true;
+
+ if (2 == scalarsPerPos
+ && SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
+ && setup.useGDI(hdc_, paint)) {
+ int startX = SkScalarRound(pos[0]);
+ int startY = SkScalarRound(pos[1] + getAscent(paint));
+ const int count = len >> 1;
+ SkAutoSTMalloc<64, INT> storage(count);
+ INT* advances = storage.get();
+ for (int i = 0; i < count - 1; ++i) {
+ advances[i] = SkScalarRound(pos[2] - pos[0]);
+ pos += 2;
+ }
+ useDrawText = !EnsureExtTextOut(hdc_, startX, startY,
+ getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text),
+ count, advances, paint.getTypeface());
+ }
+
+ if (useDrawText) {
+ size_t (*bytesPerCodePoint)(const char*);
+ switch (paint.getTextEncoding()) {
+ case SkPaint::kUTF8_TextEncoding:
+ bytesPerCodePoint = size_utf8;
+ break;
+ case SkPaint::kUTF16_TextEncoding:
+ bytesPerCodePoint = size_utf16;
+ break;
+ default:
+ SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding());
+ bytesPerCodePoint = size_glyphid;
+ break;
+ }
+
+ const char* curr = reinterpret_cast<const char*>(text);
+ const char* stop = curr + len;
+ while (curr < stop) {
+ SkScalar y = (1 == scalarsPerPos) ? constY : pos[1];
+ size_t bytes = bytesPerCodePoint(curr);
+ drawText(draw, curr, bytes, pos[0], y, paint);
+ curr += bytes;
+ pos += scalarsPerPos;
+ }
+ }
+}
+
+void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw,
+ const void* text,
+ size_t len,
+ const SkPath& path,
+ const SkMatrix* matrix,
+ const SkPaint& paint) {
+ // This function isn't used in the code. Verify this assumption.
+ SkASSERT(false);
+}
+
+void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw,
+ SkCanvas::VertexMode vmode,
+ int vertexCount,
+ const SkPoint vertices[],
+ const SkPoint texs[],
+ const SkColor colors[],
+ SkXfermode* xmode,
+ const uint16_t indices[],
+ int indexCount,
+ const SkPaint& paint) {
+ // This function isn't used in the code. Verify this assumption.
+ SkASSERT(false);
+}
+
+void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw,
+ SkDevice* device,
+ int x,
+ int y,
+ const SkPaint& paint) {
+ // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if
+ // it is a vectorial device.
+ drawSprite(draw, device->accessBitmap(false), x, y, paint);
+}
+
+bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) {
+ // Note: The goal here is to transfert the SkPaint's state to the HDC's state.
+ // This function does not execute the SkPaint drawing commands. These should
+ // be executed in drawPaint().
+
+ SkPaint::Style style = paint.getStyle();
+ if (!paint.getAlpha())
+ style = (SkPaint::Style) SkPaint::kStyleCount;
+
+ switch (style) {
+ case SkPaint::kFill_Style:
+ if (!CreateBrush(true, paint) ||
+ !CreatePen(false, paint))
+ return false;
+ break;
+ case SkPaint::kStroke_Style:
+ if (!CreateBrush(false, paint) ||
+ !CreatePen(true, paint))
+ return false;
+ break;
+ case SkPaint::kStrokeAndFill_Style:
+ if (!CreateBrush(true, paint) ||
+ !CreatePen(true, paint))
+ return false;
+ break;
+ default:
+ if (!CreateBrush(false, paint) ||
+ !CreatePen(false, paint))
+ return false;
+ break;
+ }
+
+ /*
+ getFlags();
+ isAntiAlias();
+ isDither()
+ isLinearText()
+ isSubpixelText()
+ isUnderlineText()
+ isStrikeThruText()
+ isFakeBoldText()
+ isDevKernText()
+ isFilterBitmap()
+
+ // Skia's text is not used. This should be fixed.
+ getTextAlign()
+ getTextScaleX()
+ getTextSkewX()
+ getTextEncoding()
+ getFontMetrics()
+ getFontSpacing()
+ */
+
+ // BUG 1094907: Implement shaders. Shaders currently in use:
+ // SkShader::CreateBitmapShader
+ // SkGradientShader::CreateRadial
+ // SkGradientShader::CreateLinear
+ // SkASSERT(!paint.getShader());
+
+ // http://b/1106647 Implement loopers and mask filter. Looper currently in
+ // use:
+ // SkBlurDrawLooper is used for shadows.
+ // SkASSERT(!paint.getLooper());
+ // SkASSERT(!paint.getMaskFilter());
+
+ // http://b/1165900 Implement xfermode.
+ // SkASSERT(!paint.getXfermode());
+
+ // The path effect should be processed before arriving here.
+ SkASSERT(!paint.getPathEffect());
+
+ // This isn't used in the code. Verify this assumption.
+ SkASSERT(!paint.getRasterizer());
+ // Reuse code to load Win32 Fonts.
+ return true;
+}
+
+void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform,
+ const SkRegion& region,
+ const SkClipStack&) {
+ transform_ = transform;
+ LoadTransformToDC(hdc_, transform_);
+ clip_region_ = region;
+ if (!clip_region_.isEmpty())
+ LoadClipRegion();
+}
+
+void VectorPlatformDeviceEmf::DrawToNativeContext(HDC dc, int x, int y,
+ const RECT* src_rect) {
+ SkASSERT(false);
+}
+
+void VectorPlatformDeviceEmf::LoadClipRegion() {
+ SkMatrix t;
+ t.reset();
+ LoadClippingRegionToDC(hdc_, clip_region_, t);
+}
+
+SkDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice(
+ SkBitmap::Config config, int width, int height, bool isOpaque,
+ Usage /*usage*/) {
+ SkASSERT(config == SkBitmap::kARGB_8888_Config);
+ return VectorPlatformDeviceEmf::CreateDevice(width, height, isOpaque, NULL);
+}
+
+bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) {
+ SkASSERT(previous_brush_ == NULL);
+ // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer.
+ // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use
+ // WHITE_BRUSH instead.
+
+ if (!use_brush) {
+ // Set the transparency.
+ if (0 == SetBkMode(hdc_, TRANSPARENT)) {
+ SkASSERT(false);
+ return false;
+ }
+
+ // Select the NULL brush.
+ previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH));
+ return previous_brush_ != NULL;
+ }
+
+ // Set the opacity.
+ if (0 == SetBkMode(hdc_, OPAQUE)) {
+ SkASSERT(false);
+ return false;
+ }
+
+ // Create and select the brush.
+ previous_brush_ = SelectObject(CreateSolidBrush(color));
+ return previous_brush_ != NULL;
+}
+
+bool VectorPlatformDeviceEmf::CreatePen(bool use_pen,
+ COLORREF color,
+ int stroke_width,
+ float stroke_miter,
+ DWORD pen_style) {
+ SkASSERT(previous_pen_ == NULL);
+ // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer.
+ // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN
+ // instead.
+
+ // No pen case
+ if (!use_pen) {
+ previous_pen_ = SelectObject(GetStockObject(NULL_PEN));
+ return previous_pen_ != NULL;
+ }
+
+ // Use the stock pen if the stroke width is 0.
+ if (stroke_width == 0) {
+ // Create a pen with the right color.
+ previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color));
+ return previous_pen_ != NULL;
+ }
+
+ // Load a custom pen.
+ LOGBRUSH brush;
+ brush.lbStyle = BS_SOLID;
+ brush.lbColor = color;
+ brush.lbHatch = 0;
+ HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL);
+ SkASSERT(pen != NULL);
+ previous_pen_ = SelectObject(pen);
+ if (previous_pen_ == NULL)
+ return false;
+
+ if (!SetMiterLimit(hdc_, stroke_miter, NULL)) {
+ SkASSERT(false);
+ return false;
+ }
+ return true;
+}
+
+void VectorPlatformDeviceEmf::Cleanup() {
+ if (previous_brush_) {
+ HGDIOBJ result = SelectObject(previous_brush_);
+ previous_brush_ = NULL;
+ if (result) {
+ BOOL res = DeleteObject(result);
+ SkASSERT(res != 0);
+ }
+ }
+ if (previous_pen_) {
+ HGDIOBJ result = SelectObject(previous_pen_);
+ previous_pen_ = NULL;
+ if (result) {
+ BOOL res = DeleteObject(result);
+ SkASSERT(res != 0);
+ }
+ }
+ // Remove any loaded path from the context.
+ AbortPath(hdc_);
+}
+
+HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) {
+ HGDIOBJ result = ::SelectObject(hdc_, object);
+ SkASSERT(result != HGDI_ERROR);
+ if (result == HGDI_ERROR)
+ return NULL;
+ return result;
+}
+
+bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush,
+ const SkPaint& paint) {
+ // Make sure that for transparent color, no brush is used.
+ if (paint.getAlpha() == 0) {
+ use_brush = false;
+ }
+
+ return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor()));
+}
+
+bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) {
+ // Make sure that for transparent color, no pen is used.
+ if (paint.getAlpha() == 0) {
+ use_pen = false;
+ }
+
+ DWORD pen_style = PS_GEOMETRIC | PS_SOLID;
+ switch (paint.getStrokeJoin()) {
+ case SkPaint::kMiter_Join:
+ // Connects path segments with a sharp join.
+ pen_style |= PS_JOIN_MITER;
+ break;
+ case SkPaint::kRound_Join:
+ // Connects path segments with a round join.
+ pen_style |= PS_JOIN_ROUND;
+ break;
+ case SkPaint::kBevel_Join:
+ // Connects path segments with a flat bevel join.
+ pen_style |= PS_JOIN_BEVEL;
+ break;
+ default:
+ SkASSERT(false);
+ break;
+ }
+ switch (paint.getStrokeCap()) {
+ case SkPaint::kButt_Cap:
+ // Begin/end contours with no extension.
+ pen_style |= PS_ENDCAP_FLAT;
+ break;
+ case SkPaint::kRound_Cap:
+ // Begin/end contours with a semi-circle extension.
+ pen_style |= PS_ENDCAP_ROUND;
+ break;
+ case SkPaint::kSquare_Cap:
+ // Begin/end contours with a half square extension.
+ pen_style |= PS_ENDCAP_SQUARE;
+ break;
+ default:
+ SkASSERT(false);
+ break;
+ }
+
+ return CreatePen(use_pen,
+ SkColorToCOLORREF(paint.getColor()),
+ SkScalarRound(paint.getStrokeWidth()),
+ paint.getStrokeMiter(),
+ pen_style);
+}
+
+void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap,
+ int x, int y,
+ const SkPaint& paint) {
+ unsigned char alpha = paint.getAlpha();
+ if (alpha == 0)
+ return;
+
+ bool is_translucent;
+ if (alpha != 255) {
+ // ApplyPaint expect an opaque color.
+ SkPaint tmp_paint(paint);
+ tmp_paint.setAlpha(255);
+ if (!ApplyPaint(tmp_paint))
+ return;
+ is_translucent = true;
+ } else {
+ if (!ApplyPaint(paint))
+ return;
+ is_translucent = false;
+ }
+ int src_size_x = bitmap.width();
+ int src_size_y = bitmap.height();
+ if (!src_size_x || !src_size_y)
+ return;
+
+ // Create a BMP v4 header that we can serialize. We use the shared "V3"
+ // fillter to fill the stardard items, then add in the "V4" stuff we want.
+ BITMAPV4HEADER bitmap_header;
+ memset(&bitmap_header, 0, sizeof(BITMAPV4HEADER));
+ FillBitmapInfoHeader(src_size_x, src_size_y,
+ reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header));
+ bitmap_header.bV4Size = sizeof(BITMAPV4HEADER);
+ bitmap_header.bV4RedMask = 0x00ff0000;
+ bitmap_header.bV4GreenMask = 0x0000ff00;
+ bitmap_header.bV4BlueMask = 0x000000ff;
+ bitmap_header.bV4AlphaMask = 0xff000000;
+
+ SkAutoLockPixels lock(bitmap);
+ SkASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config);
+ const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels());
+ if (pixels == NULL) {
+ SkASSERT(false);
+ return;
+ }
+
+ if (!is_translucent) {
+ int row_length = bitmap.rowBytesAsPixels();
+ // There is no quick way to determine if an image is opaque.
+ for (int y2 = 0; y2 < src_size_y; ++y2) {
+ for (int x2 = 0; x2 < src_size_x; ++x2) {
+ if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) {
+ is_translucent = true;
+ y2 = src_size_y;
+ break;
+ }
+ }
+ }
+ }
+
+ HDC dc = BeginPlatformPaint();
+ BITMAPINFOHEADER hdr;
+ FillBitmapInfoHeader(src_size_x, src_size_y, &hdr);
+ if (is_translucent) {
+ // The image must be loaded as a bitmap inside a device context.
+ HDC bitmap_dc = ::CreateCompatibleDC(dc);
+ void* bits = NULL;
+ HBITMAP hbitmap = ::CreateDIBSection(
+ bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr),
+ DIB_RGB_COLORS, &bits, NULL, 0);
+
+ // static cast to a char so we can do byte ptr arithmatic to
+ // get the offset.
+ unsigned char* dest_buffer = static_cast<unsigned char *>(bits);
+
+ // We will copy row by row to avoid having to worry about
+ // the row strides being different.
+ const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth;
+ for (int row = 0; row < bitmap.height(); ++row) {
+ int dest_offset = row * dest_row_size;
+ // pixels_offset in terms of pixel count.
+ int src_offset = row * bitmap.rowBytesAsPixels();
+ memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size);
+ }
+ SkASSERT(hbitmap);
+ HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap);
+
+ // After some analysis of IE7's behavior, this is the thing to do. I was
+ // sure IE7 was doing so kind of bitmasking due to the way translucent image
+ // where renderered but after some windbg tracing, it is being done by the
+ // printer driver after all (mostly HP printers). IE7 always use AlphaBlend
+ // for bitmasked images. The trick seems to switch the stretching mode in
+ // what the driver expects.
+ DWORD previous_mode = GetStretchBltMode(dc);
+ BOOL result = SetStretchBltMode(dc, COLORONCOLOR);
+ SkASSERT(result);
+ // Note that this function expect premultiplied colors (!)
+ BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA};
+ result = GdiAlphaBlend(dc,
+ x, y, // Destination origin.
+ src_size_x, src_size_y, // Destination size.
+ bitmap_dc,
+ 0, 0, // Source origin.
+ src_size_x, src_size_y, // Source size.
+ blend_function);
+ SkASSERT(result);
+ result = SetStretchBltMode(dc, previous_mode);
+ SkASSERT(result);
+
+ ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap));
+ DeleteObject(hbitmap);
+ DeleteDC(bitmap_dc);
+ } else {
+ int nCopied = StretchDIBits(dc,
+ x, y, // Destination origin.
+ src_size_x, src_size_y,
+ 0, 0, // Source origin.
+ src_size_x, src_size_y, // Source size.
+ pixels,
+ reinterpret_cast<const BITMAPINFO*>(&hdr),
+ DIB_RGB_COLORS,
+ SRCCOPY);
+ }
+ EndPlatformPaint();
+ Cleanup();
+}
+
+} // namespace skia
diff --git a/chromium/skia/ext/vector_platform_device_emf_win.h b/chromium/skia/ext/vector_platform_device_emf_win.h
new file mode 100644
index 00000000000..c0deeeceaeb
--- /dev/null
+++ b/chromium/skia/ext/vector_platform_device_emf_win.h
@@ -0,0 +1,140 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_VECTOR_PLATFORM_DEVICE_EMF_WIN_H_
+#define SKIA_EXT_VECTOR_PLATFORM_DEVICE_EMF_WIN_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "skia/ext/platform_device.h"
+#include "third_party/skia/include/core/SkMatrix.h"
+#include "third_party/skia/include/core/SkRegion.h"
+
+namespace skia {
+
+// A device is basically a wrapper around SkBitmap that provides a surface for
+// SkCanvas to draw into. This specific device is not not backed by a surface
+// and is thus unreadable. This is because the backend is completely vectorial.
+// This device is a simple wrapper over a Windows device context (HDC) handle.
+class VectorPlatformDeviceEmf : public SkDevice, public PlatformDevice {
+ public:
+ SK_API static SkDevice* CreateDevice(int width, int height, bool isOpaque,
+ HANDLE shared_section);
+
+ // Factory function. The DC is kept as the output context.
+ static SkDevice* create(HDC dc, int width, int height);
+
+ VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap);
+ virtual ~VectorPlatformDeviceEmf();
+
+ // PlatformDevice methods
+ virtual PlatformSurface BeginPlatformPaint() OVERRIDE;
+ virtual void DrawToNativeContext(HDC dc, int x, int y,
+ const RECT* src_rect) OVERRIDE;
+ // SkDevice methods.
+ virtual uint32_t getDeviceCapabilities();
+ virtual void drawPaint(const SkDraw& draw, const SkPaint& paint) OVERRIDE;
+ virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
+ size_t count, const SkPoint[],
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawRect(const SkDraw& draw, const SkRect& r,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawPath(const SkDraw& draw, const SkPath& path,
+ const SkPaint& paint,
+ const SkMatrix* prePathMatrix = NULL,
+ bool pathIsMutable = false) OVERRIDE;
+ virtual void drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+ const SkRect* src, const SkRect& dst,
+ const SkPaint& paint) SK_OVERRIDE;
+ virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+ const SkMatrix& matrix,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+ int x, int y, const SkPaint& paint) OVERRIDE;
+ virtual void drawText(const SkDraw& draw, const void* text, size_t len,
+ SkScalar x, SkScalar y, const SkPaint& paint) OVERRIDE;
+ virtual void drawPosText(const SkDraw& draw, const void* text, size_t len,
+ const SkScalar pos[], SkScalar constY,
+ int scalarsPerPos, const SkPaint& paint) OVERRIDE;
+ virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawVertices(const SkDraw& draw, SkCanvas::VertexMode,
+ int vertexCount,
+ const SkPoint verts[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) OVERRIDE;
+ virtual void drawDevice(const SkDraw& draw, SkDevice*, int x, int y,
+ const SkPaint&) OVERRIDE;
+
+ virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region,
+ const SkClipStack&) OVERRIDE;
+
+ void LoadClipRegion();
+
+ protected:
+ virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config, int width,
+ int height, bool isOpaque,
+ Usage usage) OVERRIDE;
+
+ private:
+ // Applies the SkPaint's painting properties in the current GDI context, if
+ // possible. If GDI can't support all paint's properties, returns false. It
+ // doesn't execute the "commands" in SkPaint.
+ bool ApplyPaint(const SkPaint& paint);
+
+ // Selects a new object in the device context. It can be a pen, a brush, a
+ // clipping region, a bitmap or a font. Returns the old selected object.
+ HGDIOBJ SelectObject(HGDIOBJ object);
+
+ // Creates a brush according to SkPaint's properties.
+ bool CreateBrush(bool use_brush, const SkPaint& paint);
+
+ // Creates a pen according to SkPaint's properties.
+ bool CreatePen(bool use_pen, const SkPaint& paint);
+
+ // Restores back the previous objects (pen, brush, etc) after a paint command.
+ void Cleanup();
+
+ // Creates a brush according to SkPaint's properties.
+ bool CreateBrush(bool use_brush, COLORREF color);
+
+ // Creates a pen according to SkPaint's properties.
+ bool CreatePen(bool use_pen, COLORREF color, int stroke_width,
+ float stroke_miter, DWORD pen_style);
+
+ // Draws a bitmap in the the device, using the currently loaded matrix.
+ void InternalDrawBitmap(const SkBitmap& bitmap, int x, int y,
+ const SkPaint& paint);
+
+ // The Windows Device Context handle. It is the backend used with GDI drawing.
+ // This backend is write-only and vectorial.
+ HDC hdc_;
+
+ // Translation assigned to the DC: we need to keep track of this separately
+ // so it can be updated even if the DC isn't created yet.
+ SkMatrix transform_;
+
+ // The current clipping
+ SkRegion clip_region_;
+
+ // Previously selected brush before the current drawing.
+ HGDIOBJ previous_brush_;
+
+ // Previously selected pen before the current drawing.
+ HGDIOBJ previous_pen_;
+
+ DISALLOW_COPY_AND_ASSIGN(VectorPlatformDeviceEmf);
+};
+
+typedef void (*SkiaEnsureTypefaceCharactersAccessible)
+ (const LOGFONT& font, const wchar_t* text, unsigned int text_length);
+
+SK_API void SetSkiaEnsureTypefaceCharactersAccessible(
+ SkiaEnsureTypefaceCharactersAccessible func);
+
+} // namespace skia
+
+#endif // SKIA_EXT_VECTOR_PLATFORM_DEVICE_EMF_WIN_H_
diff --git a/chromium/skia/ext/vector_platform_device_skia.cc b/chromium/skia/ext/vector_platform_device_skia.cc
new file mode 100644
index 00000000000..d8d3084afcb
--- /dev/null
+++ b/chromium/skia/ext/vector_platform_device_skia.cc
@@ -0,0 +1,88 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "skia/ext/vector_platform_device_skia.h"
+
+#include "skia/ext/bitmap_platform_device.h"
+#include "third_party/skia/include/core/SkClipStack.h"
+#include "third_party/skia/include/core/SkDraw.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkRegion.h"
+#include "third_party/skia/include/core/SkScalar.h"
+
+namespace skia {
+
+static inline SkBitmap makeABitmap(int width, int height) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kNo_Config, width, height);
+ return bitmap;
+}
+
+VectorPlatformDeviceSkia::VectorPlatformDeviceSkia(
+ const SkISize& pageSize,
+ const SkISize& contentSize,
+ const SkMatrix& initialTransform)
+ : SkPDFDevice(pageSize, contentSize, initialTransform) {
+ SetPlatformDevice(this, this);
+}
+
+VectorPlatformDeviceSkia::~VectorPlatformDeviceSkia() {
+}
+
+bool VectorPlatformDeviceSkia::SupportsPlatformPaint() {
+ return false;
+}
+
+PlatformSurface VectorPlatformDeviceSkia::BeginPlatformPaint() {
+ // Even when drawing a vector representation of the page, we have to
+ // provide a raster surface for plugins to render into - they don't have
+ // a vector interface. Therefore we create a BitmapPlatformDevice here
+ // and return the context from it, then layer on the raster data as an
+ // image in EndPlatformPaint.
+ DCHECK(raster_surface_ == NULL);
+ raster_surface_ = skia::AdoptRef(
+ BitmapPlatformDevice::CreateAndClear(width(), height(), false));
+ return raster_surface_->BeginPlatformPaint();
+}
+
+void VectorPlatformDeviceSkia::EndPlatformPaint() {
+ DCHECK(raster_surface_ != NULL);
+ SkPaint paint;
+ // SkPDFDevice checks the passed SkDraw for an empty clip (only). Fake
+ // it out by setting a non-empty clip.
+ SkDraw draw;
+ SkRegion clip(SkIRect::MakeWH(width(), height()));
+ draw.fClip=&clip;
+ drawSprite(draw, raster_surface_->accessBitmap(false), 0, 0, paint);
+ // BitmapPlatformDevice matches begin and end calls.
+ raster_surface_->EndPlatformPaint();
+ raster_surface_.clear();
+}
+
+#if defined(OS_WIN)
+void VectorPlatformDeviceSkia::DrawToNativeContext(HDC dc,
+ int x,
+ int y,
+ const RECT* src_rect) {
+ SkASSERT(false);
+}
+#elif defined(OS_MACOSX)
+void VectorPlatformDeviceSkia::DrawToNativeContext(CGContext* context, int x,
+ int y, const CGRect* src_rect) {
+ SkASSERT(false);
+}
+
+CGContextRef VectorPlatformDeviceSkia::GetBitmapContext() {
+ SkASSERT(false);
+ return NULL;
+}
+#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_OPENBSD)
+void VectorPlatformDeviceSkia::DrawToNativeContext(
+ PlatformSurface surface, int x, int y, const PlatformRect* src_rect) {
+ // Should never be called on Linux.
+ SkASSERT(false);
+}
+#endif
+
+} // namespace skia
diff --git a/chromium/skia/ext/vector_platform_device_skia.h b/chromium/skia/ext/vector_platform_device_skia.h
new file mode 100644
index 00000000000..4a6d2d92921
--- /dev/null
+++ b/chromium/skia/ext/vector_platform_device_skia.h
@@ -0,0 +1,60 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SKIA_EXT_VECTOR_PLATFORM_DEVICE_SKIA_H_
+#define SKIA_EXT_VECTOR_PLATFORM_DEVICE_SKIA_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "skia/ext/platform_device.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkTScopedPtr.h"
+#include "third_party/skia/include/pdf/SkPDFDevice.h"
+
+class SkMatrix;
+
+namespace skia {
+
+class BitmapPlatformDevice;
+
+class VectorPlatformDeviceSkia : public SkPDFDevice, public PlatformDevice {
+ public:
+ SK_API VectorPlatformDeviceSkia(const SkISize& pageSize,
+ const SkISize& contentSize,
+ const SkMatrix& initialTransform);
+ virtual ~VectorPlatformDeviceSkia();
+
+ // PlatformDevice methods.
+ virtual bool SupportsPlatformPaint() OVERRIDE;
+
+ virtual PlatformSurface BeginPlatformPaint() OVERRIDE;
+ virtual void EndPlatformPaint() OVERRIDE;
+#if defined(OS_WIN)
+ virtual void DrawToNativeContext(HDC dc,
+ int x,
+ int y,
+ const RECT* src_rect) OVERRIDE;
+#elif defined(OS_MACOSX)
+ virtual void DrawToNativeContext(CGContext* context,
+ int x,
+ int y,
+ const CGRect* src_rect) OVERRIDE;
+ virtual CGContextRef GetBitmapContext() OVERRIDE;
+#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_OPENBSD)
+ virtual void DrawToNativeContext(PlatformSurface surface,
+ int x,
+ int y,
+ const PlatformRect* src_rect) OVERRIDE;
+#endif
+
+ private:
+ skia::RefPtr<BitmapPlatformDevice> raster_surface_;
+
+ DISALLOW_COPY_AND_ASSIGN(VectorPlatformDeviceSkia);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_VECTOR_PLATFORM_DEVICE_SKIA_H_
diff --git a/chromium/skia/fix_for_1186198.diff b/chromium/skia/fix_for_1186198.diff
new file mode 100644
index 00000000000..6158f594a25
--- /dev/null
+++ b/chromium/skia/fix_for_1186198.diff
@@ -0,0 +1,38 @@
+Index: sgl/SkEdge.cpp
+===================================================================
+--- sgl/SkEdge.cpp (revision 42965)
++++ sgl/SkEdge.cpp (working copy)
+@@ -17,6 +17,7 @@
+
+ #include "SkEdge.h"
+ #include "SkFDot6.h"
++#include <limits>
+
+ /*
+ In setLine, setQuadratic, setCubic, the first thing we do is to convert
+@@ -76,8 +77,23 @@
+
+ fX = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63)); // + SK_Fixed1/2
+ fDX = slope;
+- fFirstY = SkToS16(top);
+- fLastY = SkToS16(bot - 1);
++ fFirstY = (int16_t)(top); // inlined skToS16()
++ if (top != (long)fFirstY) {
++ if (fFirstY < top) {
++ fFirstY = std::numeric_limits<int16_t>::max();
++ } else {
++ fFirstY = std::numeric_limits<int16_t>::min();
++ }
++ fX -= fDX * (top - (long)fFirstY);
++ }
++ fLastY = (int16_t)(bot - 1); // inlined SkToS16()
++ if (bot-1 != (long)fLastY) {
++ if (fLastY < bot-1) {
++ fLastY = std::numeric_limits<int16_t>::max();
++ } else {
++ fLastY = std::numeric_limits<int16_t>::min();
++ }
++ }
+ fCurveCount = 0;
+ fWinding = SkToS8(winding);
+ fCurveShift = 0;
diff --git a/chromium/skia/skia.gyp b/chromium/skia/skia.gyp
new file mode 100644
index 00000000000..d1a227557c6
--- /dev/null
+++ b/chromium/skia/skia.gyp
@@ -0,0 +1,139 @@
+# Copyright (c) 2012 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+{
+ 'conditions': [
+ # In component mode (shared_lib), we build all of skia as a single DLL.
+ # However, in the static mode, we need to build skia as multiple targets
+ # in order to support the use case where a platform (e.g. Android) may
+ # already have a copy of skia as a system library.
+ ['component=="static_library" and use_system_skia==0', {
+ 'targets': [
+ {
+ 'target_name': 'skia_library',
+ 'type': 'static_library',
+ 'includes': [
+ 'skia_library.gypi',
+ 'skia_common.gypi',
+ ],
+ },
+ ],
+ }],
+ ['component=="static_library" and use_system_skia==1', {
+ 'targets': [
+ {
+ 'target_name': 'skia_library',
+ 'type': 'none',
+ 'includes': ['skia_system.gypi'],
+ },
+ ],
+ }],
+ ['component=="static_library"', {
+ 'targets': [
+ {
+ 'target_name': 'skia',
+ 'type': 'none',
+ 'dependencies': [
+ 'skia_library',
+ 'skia_chrome',
+ ],
+ 'export_dependent_settings': [
+ 'skia_library',
+ 'skia_chrome',
+ ],
+ },
+ {
+ 'target_name': 'skia_chrome',
+ 'type': 'static_library',
+ 'includes': [
+ 'skia_chrome.gypi',
+ 'skia_common.gypi',
+ ],
+ },
+ ],
+ },
+ { # component != static_library
+ 'targets': [
+ {
+ 'target_name': 'skia',
+ 'type': 'shared_library',
+ 'includes': [
+ 'skia_library.gypi',
+ 'skia_chrome.gypi',
+ 'skia_common.gypi',
+ ],
+ 'defines': [
+ 'SKIA_DLL',
+ 'GR_DLL=1',
+ 'GR_IMPLEMENTATION=1',
+ 'SKIA_IMPLEMENTATION=1',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'SKIA_DLL',
+ 'GR_DLL=1',
+ ],
+ },
+ },
+ {
+ 'target_name': 'skia_library',
+ 'type': 'none',
+ },
+ {
+ 'target_name': 'skia_chrome',
+ 'type': 'none',
+ },
+ ],
+ }],
+ ],
+
+ # targets that are not dependent upon the component type
+ 'targets': [
+ {
+ 'target_name': 'skia_chrome_opts',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ 'config',
+ '../third_party/skia/include/config',
+ '../third_party/skia/include/core',
+ ],
+ 'conditions': [
+ [ 'os_posix == 1 and OS != "mac" and OS != "android" and \
+ target_arch != "arm" and target_arch != "mipsel"', {
+ 'cflags': [
+ '-msse2',
+ ],
+ }],
+ [ 'target_arch != "arm" and target_arch != "mipsel"', {
+ 'sources': [
+ 'ext/convolver_SSE2.cc',
+ ],
+ }],
+ [ 'target_arch == "mipsel"',{
+ 'cflags': [
+ '-fomit-frame-pointer',
+ ],
+ 'sources': [
+ 'ext/convolver_mips_dspr2.cc',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'image_operations_bench',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'skia',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'ext/image_operations_bench.cc',
+ ],
+ },
+ ],
+}
diff --git a/chromium/skia/skia_Prefix.pch b/chromium/skia/skia_Prefix.pch
new file mode 100644
index 00000000000..5541dea0637
--- /dev/null
+++ b/chromium/skia/skia_Prefix.pch
@@ -0,0 +1,12 @@
+// Copyright (c) 2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Prefix header for all source files in the 'Skia' framework.
+
+#ifdef __OBJC__
+#import <Cocoa/Cocoa.h>
+#endif
+
+// Include the Skia prefix file.
+#include "precompiled.cc"
diff --git a/chromium/skia/skia_chrome.gypi b/chromium/skia/skia_chrome.gypi
new file mode 100644
index 00000000000..ecd9b4735c2
--- /dev/null
+++ b/chromium/skia/skia_chrome.gypi
@@ -0,0 +1,132 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+# This gypi file contains all the Chrome-specific enhancements to Skia.
+# In component mode (shared_lib) it is folded into a single shared library with
+# the Skia files but in all other cases it is a separate library.
+{
+ 'dependencies': [
+ 'skia_library',
+ 'skia_chrome_opts',
+ '../base/base.gyp:base',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ ],
+
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'ext',
+ ],
+ },
+
+ 'include_dirs': [
+ '..',
+ ],
+
+ 'sources': [
+ 'ext/analysis_canvas.cc',
+ 'ext/analysis_canvas.h',
+ 'ext/benchmarking_canvas.cc',
+ 'ext/benchmarking_canvas.h',
+ 'ext/bitmap_platform_device.h',
+ 'ext/bitmap_platform_device_android.cc',
+ 'ext/bitmap_platform_device_android.h',
+ 'ext/bitmap_platform_device_data.h',
+ 'ext/bitmap_platform_device_linux.cc',
+ 'ext/bitmap_platform_device_linux.h',
+ 'ext/bitmap_platform_device_mac.cc',
+ 'ext/bitmap_platform_device_mac.h',
+ 'ext/bitmap_platform_device_win.cc',
+ 'ext/bitmap_platform_device_win.h',
+ 'ext/convolver.cc',
+ 'ext/convolver.h',
+ 'ext/google_logging.cc',
+ 'ext/image_operations.cc',
+ 'ext/image_operations.h',
+ 'ext/lazy_pixel_ref.cc',
+ 'ext/lazy_pixel_ref.h',
+ 'ext/lazy_pixel_ref_utils.cc',
+ 'ext/lazy_pixel_ref_utils.h',
+ 'ext/SkThread_chrome.cc',
+ 'ext/paint_simplifier.cc',
+ 'ext/paint_simplifier.h',
+ 'ext/platform_canvas.cc',
+ 'ext/platform_canvas.h',
+ 'ext/platform_device.cc',
+ 'ext/platform_device.h',
+ 'ext/platform_device_linux.cc',
+ 'ext/platform_device_mac.cc',
+ 'ext/platform_device_win.cc',
+ 'ext/recursive_gaussian_convolution.cc',
+ 'ext/recursive_gaussian_convolution.h',
+ 'ext/refptr.h',
+ 'ext/SkMemory_new_handler.cpp',
+ 'ext/skia_trace_shim.h',
+ 'ext/skia_utils_base.cc',
+ 'ext/skia_utils_base.h',
+ 'ext/skia_utils_ios.mm',
+ 'ext/skia_utils_ios.h',
+ 'ext/skia_utils_mac.mm',
+ 'ext/skia_utils_mac.h',
+ 'ext/skia_utils_win.cc',
+ 'ext/skia_utils_win.h',
+ 'ext/vector_canvas.cc',
+ 'ext/vector_canvas.h',
+ 'ext/vector_platform_device_emf_win.cc',
+ 'ext/vector_platform_device_emf_win.h',
+ 'ext/vector_platform_device_skia.cc',
+ 'ext/vector_platform_device_skia.h',
+ ],
+
+ 'conditions': [
+ # For POSIX platforms, prefer the Mutex implementation provided by Skia
+ # since it does not generate static initializers.
+ # TODO: should check if SK_USE_POSIX_THREADS is defined instead
+ [ 'OS == "android" or OS == "linux" or OS == "mac" or OS == "ios"', {
+ 'sources!': [
+ 'ext/SkThread_chrome.cc',
+ ],
+ }],
+ [ 'OS == "android"', {
+ 'sources!': [
+ 'ext/vector_platform_device_skia.cc',
+ ],
+ }],
+ ['OS == "ios"', {
+ 'sources/': [
+ ['exclude', '^ext/vector_platform_device_skia\\.'],
+ ],
+ 'dependencies!': [
+ 'skia_chrome_opts',
+ ],
+ }],
+ [ 'OS == "win"', {
+ 'sources!': [
+ 'ext/SkThread_chrome.cc',
+ ],
+ }],
+ # TODO(scottmg): http://crbug.com/177306
+ ['clang==1', {
+ 'xcode_settings': {
+ 'WARNING_CFLAGS!': [
+ # Don't warn about string->bool used in asserts.
+ '-Wstring-conversion',
+ ],
+ },
+ 'cflags!': [
+ '-Wstring-conversion',
+ ],
+ }],
+ ],
+
+ 'target_conditions': [
+ # Pull in specific linux files for android (which have been filtered out
+ # by file name rules).
+ [ 'OS == "android"', {
+ 'sources/': [
+ ['include', 'ext/platform_device_linux\\.cc$'],
+ ],
+ }],
+ ],
+}
diff --git a/chromium/skia/skia_common.gypi b/chromium/skia/skia_common.gypi
new file mode 100644
index 00000000000..616ae5bb31b
--- /dev/null
+++ b/chromium/skia/skia_common.gypi
@@ -0,0 +1,33 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This gypi file handles the removal of platform-specific files from the
+# Skia build.
+{
+ 'conditions': [
+ [ 'OS != "android"', {
+ 'sources/': [
+ ['exclude', '_android\\.(cc|cpp)$'],
+ ],
+ }],
+ [ 'OS != "ios"', {
+ 'sources/': [
+ ['exclude', '_ios\\.(cc|cpp|mm?)$'],
+ ],
+ }],
+ [ 'OS != "mac"', {
+ 'sources/': [
+ ['exclude', '_mac\\.(cc|cpp|mm?)$'],
+ ],
+ }],
+ [ 'OS != "win"', {
+ 'sources/': [ ['exclude', '_win\\.(cc|cpp)$'] ],
+ }],
+ [ 'use_glib == 0', {
+ 'sources/': [ ['exclude', '_linux\\.(cc|cpp)$'] ],
+ }],
+ ],
+
+ 'msvs_disabled_warnings': [4244, 4267, 4341, 4345, 4390, 4554, 4748, 4800],
+}
diff --git a/chromium/skia/skia_library.gypi b/chromium/skia/skia_library.gypi
new file mode 100644
index 00000000000..29005533614
--- /dev/null
+++ b/chromium/skia/skia_library.gypi
@@ -0,0 +1,472 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+# This gypi file contains the Skia library.
+# In component mode (shared_lib) it is folded into a single shared library with
+# the Chrome-specific enhancements but in all other cases it is a separate lib.
+{
+ 'dependencies': [
+ 'skia_library_opts.gyp:skia_opts',
+ '../third_party/zlib/zlib.gyp:zlib',
+ ],
+
+ 'variables': {
+ 'variables': {
+ 'conditions': [
+ ['OS== "ios"', {
+ 'skia_support_gpu': 0,
+ }, {
+ 'skia_support_gpu': 1,
+ }],
+ ['OS=="ios" or OS=="android"', {
+ 'skia_support_pdf': 0,
+ }, {
+ 'skia_support_pdf': 1,
+ }],
+ ],
+ },
+ 'skia_support_gpu': '<(skia_support_gpu)',
+ 'skia_support_pdf': '<(skia_support_pdf)',
+
+ # These two set the paths so we can include skia/gyp/core.gypi
+ 'skia_src_path': '../third_party/skia/src',
+ 'skia_include_path': '../third_party/skia/include',
+
+ # This list will contain all defines that also need to be exported to
+ # dependent components.
+ 'skia_export_defines': [
+ 'SK_ENABLE_INST_COUNT=0',
+ 'SK_SUPPORT_GPU=<(skia_support_gpu)',
+ 'GR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"',
+ ],
+
+ 'default_font_cache_limit': '(20*1024*1024)',
+
+ 'conditions': [
+ ['OS== "android"', {
+ # Android devices are typically more memory constrained, so
+ # use a smaller glyph cache.
+ 'default_font_cache_limit': '(8*1024*1024)',
+ 'skia_export_defines': [
+ 'SK_BUILD_FOR_ANDROID',
+ 'USE_CHROMIUM_SKIA',
+ ],
+ }],
+ ],
+ },
+
+ 'includes': [
+ '../third_party/skia/gyp/core.gypi',
+ '../third_party/skia/gyp/effects.gypi',
+ ],
+
+ 'sources': [
+ # this should likely be moved into src/utils in skia
+ '../third_party/skia/src/core/SkFlate.cpp',
+ # We don't want to add this to Skia's core.gypi since it is
+ # Android only. Include it here and remove it for everyone
+ # but Android later.
+ '../third_party/skia/src/core/SkPaintOptionsAndroid.cpp',
+
+ '../third_party/skia/src/ports/SkImageDecoder_empty.cpp',
+ '../third_party/skia/src/images/SkScaledBitmapSampler.cpp',
+ '../third_party/skia/src/images/SkScaledBitmapSampler.h',
+
+ '../third_party/skia/src/opts/opts_check_SSE2.cpp',
+
+ '../third_party/skia/src/pdf/SkPDFCatalog.cpp',
+ '../third_party/skia/src/pdf/SkPDFCatalog.h',
+ '../third_party/skia/src/pdf/SkPDFDevice.cpp',
+ '../third_party/skia/src/pdf/SkPDFDocument.cpp',
+ '../third_party/skia/src/pdf/SkPDFFont.cpp',
+ '../third_party/skia/src/pdf/SkPDFFont.h',
+ '../third_party/skia/src/pdf/SkPDFFormXObject.cpp',
+ '../third_party/skia/src/pdf/SkPDFFormXObject.h',
+ '../third_party/skia/src/pdf/SkPDFGraphicState.cpp',
+ '../third_party/skia/src/pdf/SkPDFGraphicState.h',
+ '../third_party/skia/src/pdf/SkPDFImage.cpp',
+ '../third_party/skia/src/pdf/SkPDFImage.h',
+ '../third_party/skia/src/pdf/SkPDFImageStream.cpp',
+ '../third_party/skia/src/pdf/SkPDFImageStream.h',
+ '../third_party/skia/src/pdf/SkPDFPage.cpp',
+ '../third_party/skia/src/pdf/SkPDFPage.h',
+ '../third_party/skia/src/pdf/SkPDFResourceDict.cpp',
+ '../third_party/skia/src/pdf/SkPDFResourceDict.h',
+ '../third_party/skia/src/pdf/SkPDFShader.cpp',
+ '../third_party/skia/src/pdf/SkPDFShader.h',
+ '../third_party/skia/src/pdf/SkPDFStream.cpp',
+ '../third_party/skia/src/pdf/SkPDFStream.h',
+ '../third_party/skia/src/pdf/SkPDFTypes.cpp',
+ '../third_party/skia/src/pdf/SkPDFTypes.h',
+ '../third_party/skia/src/pdf/SkPDFUtils.cpp',
+ '../third_party/skia/src/pdf/SkPDFUtils.h',
+
+ '../third_party/skia/src/ports/SkPurgeableMemoryBlock_none.cpp',
+
+ '../third_party/skia/src/ports/SkFontConfigInterface_android.cpp',
+ '../third_party/skia/src/ports/SkFontConfigInterface_direct.cpp',
+
+ '../third_party/skia/src/fonts/SkFontMgr_fontconfig.cpp',
+ '../third_party/skia/src/ports/SkFontHost_fontconfig.cpp',
+
+ '../third_party/skia/src/ports/SkFontHost_FreeType.cpp',
+ '../third_party/skia/src/ports/SkFontHost_FreeType_common.cpp',
+ '../third_party/skia/src/ports/SkFontHost_FreeType_common.h',
+ '../third_party/skia/src/ports/SkFontConfigParser_android.cpp',
+ '../third_party/skia/src/ports/SkFontHost_mac.cpp',
+ '../third_party/skia/src/ports/SkFontHost_win.cpp',
+ '../third_party/skia/src/ports/SkGlobalInitialization_chromium.cpp',
+ '../third_party/skia/src/ports/SkOSFile_posix.cpp',
+ '../third_party/skia/src/ports/SkOSFile_stdio.cpp',
+ '../third_party/skia/src/ports/SkOSFile_win.cpp',
+ '../third_party/skia/src/ports/SkThread_pthread.cpp',
+ '../third_party/skia/src/ports/SkThread_win.cpp',
+ '../third_party/skia/src/ports/SkTime_Unix.cpp',
+ '../third_party/skia/src/ports/SkTLS_pthread.cpp',
+ '../third_party/skia/src/ports/SkTLS_win.cpp',
+
+ '../third_party/skia/src/sfnt/SkOTTable_name.cpp',
+ '../third_party/skia/src/sfnt/SkOTTable_name.h',
+ '../third_party/skia/src/sfnt/SkOTUtils.cpp',
+ '../third_party/skia/src/sfnt/SkOTUtils.h',
+
+ '../third_party/skia/include/utils/mac/SkCGUtils.h',
+ '../third_party/skia/include/utils/SkDeferredCanvas.h',
+ '../third_party/skia/include/utils/SkMatrix44.h',
+ '../third_party/skia/src/utils/debugger/SkDebugCanvas.cpp',
+ '../third_party/skia/src/utils/debugger/SkDebugCanvas.h',
+ '../third_party/skia/src/utils/debugger/SkDrawCommand.cpp',
+ '../third_party/skia/src/utils/debugger/SkDrawCommand.h',
+ '../third_party/skia/src/utils/debugger/SkObjectParser.cpp',
+ '../third_party/skia/src/utils/debugger/SkObjectParser.h',
+ '../third_party/skia/src/utils/mac/SkCreateCGImageRef.cpp',
+ '../third_party/skia/src/utils/SkBase64.cpp',
+ '../third_party/skia/src/utils/SkBase64.h',
+ '../third_party/skia/src/utils/SkBitSet.cpp',
+ '../third_party/skia/src/utils/SkBitSet.h',
+ '../third_party/skia/src/utils/SkDeferredCanvas.cpp',
+ '../third_party/skia/src/utils/SkMatrix44.cpp',
+ '../third_party/skia/src/utils/SkNullCanvas.cpp',
+ '../third_party/skia/include/utils/SkNWayCanvas.h',
+ '../third_party/skia/src/utils/SkNWayCanvas.cpp',
+ '../third_party/skia/src/utils/SkPictureUtils.cpp',
+ '../third_party/skia/src/utils/SkProxyCanvas.cpp',
+ '../third_party/skia/src/utils/SkRTConf.cpp',
+ '../third_party/skia/include/utils/SkRTConf.h',
+ '../third_party/skia/include/pdf/SkPDFDevice.h',
+ '../third_party/skia/include/pdf/SkPDFDocument.h',
+
+ '../third_party/skia/include/ports/SkTypeface_win.h',
+
+ '../third_party/skia/include/images/SkImageRef.h',
+ '../third_party/skia/include/images/SkImageRef_GlobalPool.h',
+ '../third_party/skia/include/images/SkMovie.h',
+ '../third_party/skia/include/images/SkPageFlipper.h',
+
+ '../third_party/skia/include/utils/SkNullCanvas.h',
+ '../third_party/skia/include/utils/SkPictureUtils.h',
+ '../third_party/skia/include/utils/SkProxyCanvas.h',
+ ],
+ 'include_dirs': [
+ '..',
+ 'config',
+ '../third_party/skia/include/config',
+ '../third_party/skia/include/core',
+ '../third_party/skia/include/effects',
+ '../third_party/skia/include/images',
+ '../third_party/skia/include/lazy',
+ '../third_party/skia/include/pathops',
+ '../third_party/skia/include/pdf',
+ '../third_party/skia/include/pipe',
+ '../third_party/skia/include/ports',
+ '../third_party/skia/include/utils',
+ '../third_party/skia/src/core',
+ '../third_party/skia/src/image',
+ '../third_party/skia/src/ports',
+ '../third_party/skia/src/sfnt',
+ '../third_party/skia/src/utils',
+ '../third_party/skia/src/lazy',
+ ],
+ 'conditions': [
+ ['skia_support_gpu != 0', {
+ 'includes': [
+ '../third_party/skia/gyp/gpu.gypi',
+ ],
+ 'sources': [
+ '<@(skgpu_sources)',
+ ],
+ 'include_dirs': [
+ '../third_party/skia/include/gpu',
+ '../third_party/skia/src/gpu',
+ ],
+ }],
+ ['skia_support_pdf == 0', {
+ 'sources/': [
+ ['exclude', '../third_party/skia/src/pdf/']
+ ],
+ }],
+ ['skia_support_pdf == 1', {
+ 'dependencies': [
+ '../third_party/sfntly/sfntly.gyp:sfntly',
+ ],
+ }],
+
+ #Settings for text blitting, chosen to approximate the system browser.
+ [ 'OS == "linux"', {
+ 'defines': [
+ 'SK_GAMMA_EXPONENT=1.2',
+ 'SK_GAMMA_CONTRAST=0.2',
+ ],
+ }],
+ ['OS == "android"', {
+ 'defines': [
+ 'SK_GAMMA_APPLY_TO_A8',
+ 'SK_GAMMA_EXPONENT=1.4',
+ 'SK_GAMMA_CONTRAST=0.0',
+ ],
+ }],
+ ['OS == "win"', {
+ 'defines': [
+ 'SK_GAMMA_SRGB',
+ 'SK_GAMMA_CONTRAST=0.5',
+ ],
+ }],
+ ['OS == "mac"', {
+ 'defines': [
+ 'SK_GAMMA_SRGB',
+ 'SK_GAMMA_CONTRAST=0.0',
+ ],
+ }],
+
+ # For POSIX platforms, prefer the Mutex implementation provided by Skia
+ # since it does not generate static initializers.
+ [ 'OS == "android" or OS == "linux" or OS == "mac" or OS == "ios"', {
+ 'defines+': [
+ 'SK_USE_POSIX_THREADS',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'SK_USE_POSIX_THREADS',
+ ],
+ },
+ }],
+
+ [ 'OS != "android"', {
+ 'sources!': [
+ '../third_party/skia/src/core/SkPaintOptionsAndroid.cpp',
+ ],
+ }],
+ [ 'OS != "ios"', {
+ 'dependencies': [
+ '../third_party/WebKit/public/blink_skia_config.gyp:blink_skia_config',
+ ],
+ 'export_dependent_settings': [
+ '../third_party/WebKit/public/blink_skia_config.gyp:blink_skia_config',
+ ],
+ }],
+ [ 'OS != "mac"', {
+ 'sources/': [
+ ['exclude', '/mac/']
+ ],
+ }],
+ [ 'target_arch == "arm" and arm_version >= 7 and arm_neon == 1', {
+ 'defines': [
+ '__ARM_HAVE_NEON',
+ ],
+ }],
+ [ 'target_arch == "arm" and arm_version >= 7 and arm_neon_optional == 1', {
+ 'defines': [
+ '__ARM_HAVE_OPTIONAL_NEON_SUPPORT',
+ ],
+ }],
+ [ 'OS == "android" and target_arch == "arm"', {
+ 'sources': [
+ '../third_party/skia/src/core/SkUtilsArm.cpp',
+ ],
+ 'includes': [
+ '../build/android/cpufeatures.gypi',
+ ],
+ }],
+ [ 'target_arch == "arm" or target_arch == "mipsel"', {
+ 'sources!': [
+ '../third_party/skia/src/opts/opts_check_SSE2.cpp'
+ ],
+ }],
+ [ 'use_glib == 1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:fontconfig',
+ '../build/linux/system.gyp:freetype2',
+ '../build/linux/system.gyp:pangocairo',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'cflags': [
+ '-Wno-unused',
+ '-Wno-unused-function',
+ ],
+ }],
+ [ 'use_glib == 0', {
+ 'sources!': [
+ '../third_party/skia/src/ports/SkFontConfigInterface_direct.cpp',
+ '../third_party/skia/src/fonts/SkFontMgr_fontconfig.cpp',
+ ],
+ }],
+ [ 'use_glib == 0 and OS != "android"', {
+ 'sources!': [
+ '../third_party/skia/src/ports/SkFontHost_FreeType.cpp',
+ '../third_party/skia/src/ports/SkFontHost_FreeType_common.cpp',
+ '../third_party/skia/src/ports/SkFontHost_fontconfig.cpp',
+
+ ],
+ }],
+ [ 'OS == "android"', {
+ 'dependencies': [
+ '../third_party/expat/expat.gyp:expat',
+ '../third_party/freetype/freetype.gyp:ft2',
+ ],
+ # This exports a hard dependency because it needs to run its
+ # symlink action in order to expose the skia header files.
+ 'hard_dependency': 1,
+ 'include_dirs': [
+ '../third_party/expat/files/lib',
+ ],
+ }],
+ [ 'OS == "ios"', {
+ 'defines': [
+ 'SK_BUILD_FOR_IOS',
+ 'SK_USE_MAC_CORE_TEXT',
+ ],
+ 'include_dirs': [
+ '../third_party/skia/include/utils/ios',
+ '../third_party/skia/include/utils/mac',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/ImageIO.framework',
+ ],
+ },
+ 'sources': [
+ # This file is used on both iOS and Mac, so it should be removed
+ # from the ios and mac conditions and moved into the main sources
+ # list.
+ '../third_party/skia/src/utils/mac/SkStream_mac.cpp',
+ ],
+ 'sources/': [
+ ['exclude', 'opts_check_SSE2\\.cpp$'],
+ ],
+
+ # The main skia_opts target does not currently work on iOS because the
+ # target architecture on iOS is determined at compile time rather than
+ # gyp time (simulator builds are x86, device builds are arm). As a
+ # temporary measure, this is a separate opts target for iOS-only, using
+ # the _none.cpp files to avoid architecture-dependent implementations.
+ 'dependencies': [
+ 'skia_library_opts.gyp:skia_opts_none',
+ ],
+ 'dependencies!': [
+ 'skia_library_opts.gyp:skia_opts',
+ ],
+ }],
+ [ 'OS == "mac"', {
+ 'defines': [
+ 'SK_BUILD_FOR_MAC',
+ 'SK_USE_MAC_CORE_TEXT',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ '../third_party/skia/include/utils/mac',
+ ],
+ },
+ 'include_dirs': [
+ '../third_party/skia/include/utils/mac',
+ ],
+ 'link_settings': {
+ 'libraries': [
+ '$(SDKROOT)/System/Library/Frameworks/AppKit.framework',
+ ],
+ },
+ 'sources': [
+ '../third_party/skia/src/utils/mac/SkStream_mac.cpp',
+ ],
+ }],
+ [ 'OS == "win"', {
+ 'sources!': [
+ '../third_party/skia/src/ports/SkOSFile_posix.cpp',
+ '../third_party/skia/src/ports/SkThread_pthread.cpp',
+ '../third_party/skia/src/ports/SkTime_Unix.cpp',
+ '../third_party/skia/src/ports/SkTLS_pthread.cpp',
+ ],
+ }],
+ # TODO(scottmg): http://crbug.com/177306
+ ['clang==1', {
+ 'xcode_settings': {
+ 'WARNING_CFLAGS!': [
+ # Don't warn about string->bool used in asserts.
+ '-Wstring-conversion',
+ ],
+ },
+ 'cflags!': [
+ '-Wstring-conversion',
+ ],
+ }],
+ ],
+ 'target_conditions': [
+ # Pull in specific Mac files for iOS (which have been filtered out
+ # by file name rules).
+ [ 'OS == "ios"', {
+ 'sources/': [
+ ['include', 'SkFontHost_mac\\.cpp$',],
+ ['include', 'SkStream_mac\\.cpp$',],
+ ['include', 'SkCreateCGImageRef\\.cpp$',],
+ ],
+ }],
+ ],
+
+ 'defines': [
+ '<@(skia_export_defines)',
+
+ # this flag can be removed entirely once this has baked for a while
+ 'SK_ALLOW_OVER_32K_BITMAPS',
+
+ # skia uses static initializers to initialize the serialization logic
+ # of its "pictures" library. This is currently not used in chrome; if
+ # it ever gets used the processes that use it need to call
+ # SkGraphics::Init().
+ 'SK_ALLOW_STATIC_GLOBAL_INITIALIZERS=0',
+
+ # Disable this check because it is too strict for some Chromium-specific
+ # subclasses of SkPixelRef. See bug: crbug.com/171776.
+ 'SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK',
+
+ 'IGNORE_ROT_AA_RECT_OPT',
+
+ 'SKIA_IGNORE_GPU_MIPMAPS',
+
+ 'SK_GDI_ALWAYS_USE_TEXTMETRICS_FOR_FONT_METRICS',
+
+ 'SK_DEFAULT_FONT_CACHE_LIMIT=<(default_font_cache_limit)',
+ ],
+
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ #temporary until we can hide SkFontHost
+ '../third_party/skia/src/core',
+
+ 'config',
+ '../third_party/skia/include/config',
+ '../third_party/skia/include/core',
+ '../third_party/skia/include/effects',
+ '../third_party/skia/include/pdf',
+ '../third_party/skia/include/gpu',
+ '../third_party/skia/include/lazy',
+ '../third_party/skia/include/pathops',
+ '../third_party/skia/include/pipe',
+ '../third_party/skia/include/ports',
+ '../third_party/skia/include/utils',
+ ],
+ 'defines': [
+ '<@(skia_export_defines)',
+ ],
+ },
+}
diff --git a/chromium/skia/skia_library_opts.gyp b/chromium/skia/skia_library_opts.gyp
new file mode 100644
index 00000000000..c87b9821bf8
--- /dev/null
+++ b/chromium/skia/skia_library_opts.gyp
@@ -0,0 +1,242 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+# This gyp file contains the platform-specific optimizations for Skia
+{
+ 'targets': [
+ # Due to an unfortunate intersection of lameness between gcc and gyp,
+ # we have to build the *_SSE2.cpp files in a separate target. The
+ # gcc lameness is that, in order to compile SSE2 intrinsics code, it
+ # must be passed the -msse2 flag. However, with this flag, it may
+ # emit SSE2 instructions even for scalar code, such as the CPUID
+ # test used to test for the presence of SSE2. So that, and all other
+ # code must be compiled *without* -msse2. The gyp lameness is that it
+ # does not allow file-specific CFLAGS, so we must create this extra
+ # target for those files to be compiled with -msse2.
+ #
+ # This is actually only a problem on 32-bit Linux (all Intel Macs have
+ # SSE2, Linux x86_64 has SSE2 by definition, and MSC will happily emit
+ # SSE2 from instrinsics, which generating plain ol' 386 for everything
+ # else). However, to keep the .gyp file simple and avoid platform-specific
+ # build breakage, we do this on all platforms.
+
+ # For about the same reason, we need to compile the ARM opts files
+ # separately as well.
+ {
+ 'target_name': 'skia_opts',
+ 'type': 'static_library',
+ 'include_dirs': [
+ 'config',
+ '../third_party/skia/include/config',
+ '../third_party/skia/include/core',
+ '../third_party/skia/src/core',
+ '../third_party/skia/src/opts',
+ ],
+ 'conditions': [
+ [ 'os_posix == 1 and OS != "mac" and OS != "android" and \
+ target_arch != "arm" and target_arch != "mipsel"', {
+ 'cflags': [
+ '-msse2',
+ ],
+ }],
+ [ 'target_arch != "arm" and target_arch != "mipsel"', {
+ 'sources': [
+ '../third_party/skia/src/opts/SkBitmapProcState_opts_SSE2.cpp',
+ '../third_party/skia/src/opts/SkBlitRect_opts_SSE2.cpp',
+ '../third_party/skia/src/opts/SkBlitRow_opts_SSE2.cpp',
+ '../third_party/skia/src/opts/SkUtils_opts_SSE2.cpp',
+ '../third_party/skia/src/opts/SkBitmapFilter_opts_SSE2.cpp',
+ ],
+ 'dependencies': [
+ 'skia_opts_ssse3',
+ ],
+ }],
+ [ 'target_arch == "arm"', {
+ 'conditions': [
+ [ 'arm_version >= 7 and arm_neon == 1', {
+ 'defines': [
+ '__ARM_HAVE_NEON',
+ ],
+ }],
+ [ 'arm_version >= 7 and arm_neon_optional == 1', {
+ 'defines': [
+ '__ARM_HAVE_OPTIONAL_NEON_SUPPORT',
+ ],
+ }],
+ [ 'arm_version >= 7 and (arm_neon == 1 or arm_neon_optional == 1)', {
+ 'cflags': [
+ # The neon assembly contains conditional instructions which
+ # aren't enclosed in an IT block. The assembler complains
+ # without this option.
+ # See #86592.
+ '-Wa,-mimplicit-it=always',
+ ],
+ 'dependencies': [
+ 'skia_opts_neon',
+ ]
+ }],
+ ],
+ # The assembly uses the frame pointer register (r7 in Thumb/r11 in
+ # ARM), the compiler doesn't like that. Explicitly remove the
+ # -fno-omit-frame-pointer flag for Android, as that gets added to all
+ # targets via common.gypi.
+ 'cflags!': [
+ '-fno-omit-frame-pointer',
+ '-marm',
+ '-mapcs-frame',
+ ],
+ 'cflags': [
+ '-fomit-frame-pointer',
+ ],
+ 'sources': [
+ '../third_party/skia/src/opts/SkBitmapProcState_opts_arm.cpp',
+ ],
+ }],
+ [ 'target_arch == "arm" and (arm_version < 7 or (arm_neon == 0 and arm_neon_optional == 1))', {
+ 'sources': [
+ '../third_party/skia/src/opts/memset.arm.S',
+ ],
+ }],
+ [ 'target_arch == "arm" and arm_version < 6', {
+ 'sources': [
+ '../third_party/skia/src/opts/SkBlitRow_opts_none.cpp',
+ '../third_party/skia/src/opts/SkUtils_opts_none.cpp',
+ ],
+ }],
+ [ 'target_arch == "arm" and arm_version >= 6', {
+ 'sources': [
+ '../third_party/skia/src/opts/SkBlitRow_opts_arm.cpp',
+ '../third_party/skia/src/opts/SkBlitRow_opts_arm.h',
+ '../third_party/skia/src/opts/opts_check_arm.cpp',
+ ],
+ }],
+ [ 'target_arch == "mipsel"',{
+ 'cflags': [
+ '-fomit-frame-pointer',
+ ],
+ 'sources': [
+ '../third_party/skia/src/opts/SkBitmapProcState_opts_none.cpp',
+ '../third_party/skia/src/opts/SkBlitRow_opts_none.cpp',
+ '../third_party/skia/src/opts/SkUtils_opts_none.cpp',
+ ],
+ }],
+ ],
+ },
+ # For the same lame reasons as what is done for skia_opts, we have to
+ # create another target specifically for SSSE3 code as we would not want
+ # to compile the SSE2 code with -mssse3 which would potentially allow
+ # gcc to generate SSSE3 code.
+ {
+ 'target_name': 'skia_opts_ssse3',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ 'config',
+ '../third_party/skia/include/config',
+ '../third_party/skia/include/core',
+ '../third_party/skia/src/core',
+ ],
+ 'conditions': [
+ [ 'OS in ["linux", "freebsd", "openbsd", "solaris", "android"]', {
+ 'cflags': [
+ '-mssse3',
+ ],
+ }],
+ [ 'OS == "mac"', {
+ 'xcode_settings': {
+ 'GCC_ENABLE_SUPPLEMENTAL_SSE3_INSTRUCTIONS': 'YES',
+ },
+ }],
+ [ 'OS == "win"', {
+ 'include_dirs': [
+ 'config/win',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'config/win',
+ ],
+ },
+ }],
+ [ 'target_arch != "arm" and target_arch != "mipsel"', {
+ 'sources': [
+ '../third_party/skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'skia_opts_none',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ 'config',
+ '../third_party/skia/include/config',
+ '../third_party/skia/include/core',
+ '../third_party/skia/src/core',
+ ],
+ 'sources': [
+ '../third_party/skia/src/opts/SkBitmapProcState_opts_none.cpp',
+ '../third_party/skia/src/opts/SkBlitRow_opts_none.cpp',
+ '../third_party/skia/src/opts/SkUtils_opts_none.cpp',
+ ],
+ },
+ ],
+ 'conditions': [
+ # NEON code must be compiled with -mfpu=neon which also affects scalar
+ # code. To support dynamic NEON code paths, we need to build all
+ # NEON-specific sources in a separate static library. The situation
+ # is very similar to the SSSE3 one.
+ ['target_arch == "arm" and (arm_neon == 1 or arm_neon_optional == 1)', {
+ 'targets': [
+ {
+ 'target_name': 'skia_opts_neon',
+ 'type': 'static_library',
+ 'include_dirs': [
+ '..',
+ 'config',
+ '../third_party/skia/include/core',
+ '../third_party/skia/src/core',
+ '../third_party/skia/src/opts',
+ ],
+ 'cflags!': [
+ '-fno-omit-frame-pointer',
+ '-mfpu=vfp', # remove them all, just in case.
+ '-mfpu=vfpv3',
+ '-mfpu=vfpv3-d16',
+ ],
+ 'cflags': [
+ '-mfpu=neon',
+ '-fomit-frame-pointer',
+ ],
+ 'ldflags': [
+ '-march=armv7-a',
+ '-Wl,--fix-cortex-a8',
+ ],
+ 'sources': [
+ '../third_party/skia/src/opts/memset16_neon.S',
+ '../third_party/skia/src/opts/memset32_neon.S',
+ '../third_party/skia/src/opts/SkBitmapProcState_arm_neon.cpp',
+ '../third_party/skia/src/opts/SkBitmapProcState_matrixProcs_neon.cpp',
+ '../third_party/skia/src/opts/SkBitmapProcState_matrix_clamp_neon.h',
+ '../third_party/skia/src/opts/SkBitmapProcState_matrix_repeat_neon.h',
+ '../third_party/skia/src/opts/SkBlitRow_opts_arm_neon.cpp',
+ ],
+ 'conditions': [
+ ['arm_neon == 1', {
+ 'defines': [
+ '__ARM_HAVE_NEON',
+ ],
+ }],
+ ['arm_neon_optional == 1', {
+ 'defines': [
+ '__ARM_HAVE_OPTIONAL_NEON_SUPPORT',
+ ],
+ }],
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/chromium/skia/skia_system.gypi b/chromium/skia/skia_system.gypi
new file mode 100644
index 00000000000..b05623c0500
--- /dev/null
+++ b/chromium/skia/skia_system.gypi
@@ -0,0 +1,24 @@
+# Copyright 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+# This gypi file contains the shim header generation and other settings to use
+# the system version of skia on Android.
+{
+ 'direct_dependent_settings': {
+ # This makes the Android build system set the include path appropriately.
+ 'libraries': [ '-lskia' ],
+ },
+ 'link_settings': {
+ # This actually causes the final binary to be linked against skia.
+ 'libraries': [ '-lskia' ],
+ },
+ 'variables': {
+ 'headers_root_path': '../third_party/skia/include',
+ },
+ 'includes': [
+ '../third_party/skia/gyp/public_headers.gypi',
+ '../build/shim_headers.gypi',
+ ],
+}
diff --git a/chromium/skia/skia_test_expectations.txt b/chromium/skia/skia_test_expectations.txt
new file mode 100644
index 00000000000..83f6ac54e64
--- /dev/null
+++ b/chromium/skia/skia_test_expectations.txt
@@ -0,0 +1,70 @@
+# TEMPORARY overrides of
+# src/third_party/WebKit/LayoutTests/platform/chromium/test_expectations.txt
+# that are associated with changes to the Skia code.
+#
+# GUIDELINES:
+# - This file should be empty most of the time.
+# - Expectations should only be added TEMPORARILY, as a step towards
+# rebaselining layout test results. If any one expectation remains in here
+# for more than a week or two, then we are probably doing something wrong.
+# - Expectations from this file should NOT be rolled into any other
+# test_expectations file. If there is a test that we expect to fail
+# indefinitely, then we should add that test to the roach motel that is
+# src/third_party/WebKit/LayoutTests/platform/chromium/test_expectations.txt
+# - Tests listed in this file should NOT be rebaselined by WebKit Gardeners,
+# unless they have made arrangements with Skia developers.
+#
+# For more information, see https://bugs.webkit.org/show_bug.cgi?id=86749
+# or email skia-dev@google.com .
+#
+# INSTRUCTIONS:
+# If you are rolling Skia's DEPS within Chrome, and trybot results indicate
+# that the DEPS roll would break some webkit layout_tests, please follow
+# these steps:
+#
+# 1. Confirm that those layout_test failures are "reasonable"-- Are they
+# actually improvements, not regressions? Or maybe they are very minor
+# differences that go along with a performance improvement?
+# If not, please fix Skia rather than rolling in the version that will
+# regress the webkit layout_tests.
+#
+# 2. File a bug to yourself to track the rebaselining of results caused by
+# your Skia DEPS roll.
+#
+# 3. Add one or more lines to this file, in the same syntax used in the main
+# test_expectations file, to mark those tests as expected-to-fail.
+# Add this file to your DEPS roll CL.
+#
+# 4. Run your DEPS roll CL through the trybots again, and confirm your CL does
+# not cause any layout tests to fail. (If there are still failures as a
+# result of your CL, you probably didn't add the test expectations correctly.)
+#
+# 5. Commit your DEPS roll CL, and keep an eye on the waterfall bots to make
+# sure nothing goes red.
+#
+# 6. Make sure to rebaseline the layout tests as soon as possible! The longer
+# we leave overrides in this file, the harder it will be to rebaseline those
+# tests (because other rendering changes might creep in).
+#
+# START OVERRIDES HERE
+
+# Image scaling difference due to ongoing Skia image resizing changes
+crbug.com/263331 virtual/deferred/fast/images/webp-color-profile-lossy.html [ ImageOnlyFailure ]
+
+# With Skia r10399 Skia's Windows text metrics have been improved
+crbug.com/265448 [ Win ] svg/batik/text/textEffect3.svg [ ImageOnlyFailure ]
+crbug.com/265448 [ Win ] svg/custom/use-referencing-nonexisting-symbol.svg [ ImageOnlyFailure ]
+crbug.com/265448 [ Win ] svg/transforms/animated-path-inside-transformed-html.xhtml [ ImageOnlyFailure ]
+crbug.com/265448 [ Win ] svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ ImageOnlyFailure ]
+crbug.com/265448 [ Win ] svg/transforms/text-with-pattern-with-svg-transform.svg [ ImageOnlyFailure ]
+
+# With skia r10444 some imperceptible differences on the edges of blurs were caused
+crbug.com/266315 fast/box-shadow/box-shadow-clipped-slices.html [ ImageOnlyFailure ]
+crbug.com/266315 fast/repaint/box-shadow-h.html [ ImageOnlyFailure ]
+crbug.com/266315 fast/repaint/box-shadow-v.html [ ImageOnlyFailure ]
+crbug.com/266315 fast/repaint/shadow-multiple-horizontal.html [ ImageOnlyFailure ]
+crbug.com/266315 fast/repaint/shadow-multiple-strict-horizontal.html [ ImageOnlyFailure ]
+crbug.com/266315 fast/repaint/shadow-multiple-strict-vertical.html [ ImageOnlyFailure ]
+crbug.com/266315 fast/repaint/shadow-multiple-vertical.html [ ImageOnlyFailure ]
+
+# END OVERRIDES HERE (this line ensures that the file is newline-terminated)
diff --git a/chromium/skia/tile_patch.diff b/chromium/skia/tile_patch.diff
new file mode 100644
index 00000000000..78118416b47
--- /dev/null
+++ b/chromium/skia/tile_patch.diff
@@ -0,0 +1,284 @@
+Index: sgl/SkBitmapProcState.h
+===================================================================
+--- sgl/SkBitmapProcState.h (revision 42716)
++++ sgl/SkBitmapProcState.h (working copy)
+@@ -39,8 +39,9 @@
+ int count,
+ uint16_t colors[]);
+
+- typedef U16CPU (*FixedTileProc)(SkFixed); // returns 0..0xFFFF
+-
++ typedef SkFixed (*FixedTileProc)(SkFixed, int);
++ typedef int (*IntTileProc)(int, int);
++
+ MatrixProc fMatrixProc; // chooseProcs
+ SampleProc32 fSampleProc32; // chooseProcs
+ SampleProc16 fSampleProc16; // chooseProcs
+@@ -48,6 +49,8 @@
+ SkMatrix fUnitInvMatrix; // chooseProcs
+ FixedTileProc fTileProcX; // chooseProcs
+ FixedTileProc fTileProcY; // chooseProcs
++ IntTileProc iTileProcX; // chooseProcs
++ IntTileProc iTileProcY; // chooseProcs
+ SkFixed fFilterOneX;
+ SkFixed fFilterOneY;
+
+Index: sgl/SkBitmapProcState.cpp
+===================================================================
+--- sgl/SkBitmapProcState.cpp (revision 42716)
++++ sgl/SkBitmapProcState.cpp (working copy)
+@@ -296,8 +296,9 @@
+ }
+ const SkMatrix* m;
+
+- if (SkShader::kClamp_TileMode == fTileModeX &&
+- SkShader::kClamp_TileMode == fTileModeY) {
++ if (inv.getType() <= SkMatrix::kTranslate_Mask ||
++ (SkShader::kClamp_TileMode == fTileModeX &&
++ SkShader::kClamp_TileMode == fTileModeY)) {
+ m = &inv;
+ } else {
+ fUnitInvMatrix = inv;
+@@ -330,6 +331,16 @@
+ fInvMatrix = m;
+ fInvProc = m->getMapXYProc();
+ fInvType = m->getType();
++ if (fInvType <= SkMatrix::kTranslate_Mask &&
++ inv.getType() > SkMatrix::kTranslate_Mask) {
++ SkASSERT(inv.getType() <=
++ (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
++ // It is possible that by the calculation of fUnitInvMatrix, we have
++ // eliminated the scale transformation of the matrix (e.g., if inv^(-1)
++ // scales fOrigBitmap into an 1X1 rect). We add the scale flag back so
++ // that we don't make wrong choice in chooseMatrixProc().
++ fInvType |= SkMatrix::kScale_Mask;
++ }
+ fInvSx = SkScalarToFixed(m->getScaleX());
+ fInvSy = SkScalarToFixed(m->getScaleY());
+ fInvKy = SkScalarToFixed(m->getSkewY());
+Index: sgl/SkBitmapProcState_matrix.h
+===================================================================
+--- sgl/SkBitmapProcState_matrix.h (revision 42716)
++++ sgl/SkBitmapProcState_matrix.h (working copy)
+@@ -1,4 +1,5 @@
+
++#define TRANSLATE_NOFILTER_NAME MAKENAME(_nofilter_translate)
+ #define SCALE_NOFILTER_NAME MAKENAME(_nofilter_scale)
+ #define SCALE_FILTER_NAME MAKENAME(_filter_scale)
+ #define AFFINE_NOFILTER_NAME MAKENAME(_nofilter_affine)
+@@ -17,6 +18,38 @@
+ #define PREAMBLE_ARG_Y
+ #endif
+
++#ifndef PREAMBLE_TRANS
++ #define PREAMBLE_TRANS(state)
++#endif
++
++static void TRANSLATE_NOFILTER_NAME(const SkBitmapProcState& s,
++ uint32_t xy[], int count, int x, int y)
++{
++ SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
++
++ PREAMBLE_TRANS(s);
++
++ x += SkScalarFloor(s.fInvMatrix->getTranslateX());
++ y += SkScalarFloor(s.fInvMatrix->getTranslateY());
++
++ *xy++ = (uint32_t)TILEY_TRANS(y, (s.fBitmap->height() - 1));
++
++ int maxX = s.fBitmap->width() - 1;
++ int i;
++ uint16_t* xx = (uint16_t*)xy;
++ for (i = (count >> 2); i > 0; --i)
++ {
++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++;
++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++;
++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++;
++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++;
++ }
++ for (i = (count & 3); i > 0; --i)
++ {
++ *xx++ = (uint16_t)TILEX_TRANS(x, maxX); x++;
++ }
++}
++
+ static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
+ uint32_t xy[], int count, int x, int y) {
+ SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+@@ -206,9 +239,9 @@
+ unsigned maxY = s.fBitmap->height() - 1;
+
+ do {
+- *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneX PREAMBLE_ARG_Y);
++ *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
+ fy += dy;
+- *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneY PREAMBLE_ARG_X);
++ *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
+ fx += dx;
+ } while (--count != 0);
+ }
+@@ -241,6 +274,9 @@
+ }
+
+ static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
++ TRANSLATE_NOFILTER_NAME,
++ TRANSLATE_NOFILTER_NAME, // No need to do filtering if the matrix is no
++ // more complex than identity/translate.
+ SCALE_NOFILTER_NAME,
+ SCALE_FILTER_NAME,
+ AFFINE_NOFILTER_NAME,
+@@ -255,7 +291,10 @@
+ #ifdef CHECK_FOR_DECAL
+ #undef CHECK_FOR_DECAL
+ #endif
+-
++#undef TILEX_TRANS
++#undef TILEY_TRANS
++
++#undef TRANSLATE_NOFILTER_NAME
+ #undef SCALE_NOFILTER_NAME
+ #undef SCALE_FILTER_NAME
+ #undef AFFINE_NOFILTER_NAME
+@@ -268,6 +307,7 @@
+ #undef PREAMBLE_PARAM_Y
+ #undef PREAMBLE_ARG_X
+ #undef PREAMBLE_ARG_Y
++#undef PREAMBLE_TRANS
+
+ #undef TILEX_LOW_BITS
+ #undef TILEY_LOW_BITS
+Index: sgl/SkBitmapProcState_matrixProcs.cpp
+===================================================================
+--- sgl/SkBitmapProcState_matrixProcs.cpp (revision 42716)
++++ sgl/SkBitmapProcState_matrixProcs.cpp (working copy)
+@@ -28,6 +28,8 @@
+ #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
+ #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
+ #define CHECK_FOR_DECAL
++#define TILEX_TRANS(x, max) SkClampMax(x, max)
++#define TILEY_TRANS(y, max) SkClampMax(y, max)
+ #include "SkBitmapProcState_matrix.h"
+
+ #define MAKENAME(suffix) RepeatX_RepeatY ## suffix
+@@ -35,6 +37,9 @@
+ #define TILEY_PROCF(fy, max) (((fy) & 0xFFFF) * ((max) + 1) >> 16)
+ #define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+ #define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
++#define REAL_MOD(val, modulus) (((val)%(modulus)) + (modulus)*( (val)<0 ))
++#define TILEX_TRANS(x, max) (REAL_MOD((x), ((max) + 1)))
++#define TILEY_TRANS(y, max) (REAL_MOD((y), ((max) + 1)))
+ #include "SkBitmapProcState_matrix.h"
+
+ #define MAKENAME(suffix) GeneralXY ## suffix
+@@ -44,13 +49,17 @@
+ #define PREAMBLE_PARAM_Y , SkBitmapProcState::FixedTileProc tileProcY
+ #define PREAMBLE_ARG_X , tileProcX
+ #define PREAMBLE_ARG_Y , tileProcY
+-#define TILEX_PROCF(fx, max) (tileProcX(fx) * ((max) + 1) >> 16)
+-#define TILEY_PROCF(fy, max) (tileProcY(fy) * ((max) + 1) >> 16)
+-#define TILEX_LOW_BITS(fx, max) ((tileProcX(fx) * ((max) + 1) >> 12) & 0xF)
+-#define TILEY_LOW_BITS(fy, max) ((tileProcY(fy) * ((max) + 1) >> 12) & 0xF)
++#define TILEX_PROCF(fx, max) (tileProcX(fx, max) >> 16)
++#define TILEY_PROCF(fy, max) (tileProcY(fy, max) >> 16)
++#define TILEX_LOW_BITS(fx, max) ((tileProcX(fx, max) >> 14) & 0x3)
++#define TILEY_LOW_BITS(fy, max) ((tileProcY(fy, max) >> 14) & 0x3)
++#define PREAMBLE_TRANS(state) SkBitmapProcState::IntTileProc tileProcX = (state).iTileProcX; \
++ SkBitmapProcState::IntTileProc tileProcY = (state).iTileProcY
++#define TILEX_TRANS(x, max) tileProcX(x, max)
++#define TILEY_TRANS(y, max) tileProcY(y, max)
+ #include "SkBitmapProcState_matrix.h"
+
+-static inline U16CPU fixed_clamp(SkFixed x)
++static inline SkFixed fixed_clamp(SkFixed x, int max)
+ {
+ #ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+ if (x >> 16)
+@@ -66,19 +75,20 @@
+ x = 0xFFFF;
+ }
+ #endif
+- return x;
++ return x * (max + 1);
+ }
+
+-static inline U16CPU fixed_repeat(SkFixed x)
++static inline SkFixed fixed_repeat(SkFixed x, int max)
+ {
+- return x & 0xFFFF;
++ return (x & 0xFFFF) * (max + 1);
+ }
+
+-static inline U16CPU fixed_mirror(SkFixed x)
++static inline SkFixed fixed_mirror(SkFixed x, int max)
+ {
+ SkFixed s = x << 15 >> 31;
+ // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
+- return (x ^ s) & 0xFFFF;
++ x = ((x ^ s) & 0xFFFF) * (max + 1);
++ return s ? (x ^ 0xFFFF) : x;
+ }
+
+ static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m)
+@@ -90,15 +100,52 @@
+ SkASSERT(SkShader::kMirror_TileMode == m);
+ return fixed_mirror;
+ }
++
++static inline int int_clamp(int x, int max)
++{
++ SkASSERT(max >= 0);
++
++ return SkClampMax(x, max);
++}
+
++static inline int int_repeat(int x, int max)
++{
++ SkASSERT(max >= 0);
++
++ return x % (max + 1);
++}
++
++static inline int int_mirror(int x, int max)
++{
++ SkASSERT(max >= 0);
++
++ int dx = x % (max + 1);
++ if (dx < 0)
++ dx = -dx - 1;
++
++ return (x / (max + 1) % 2) ? max - dx : dx;
++}
++
++static SkBitmapProcState::IntTileProc choose_int_tile_proc(unsigned m)
++{
++ if (SkShader::kClamp_TileMode == m)
++ return int_clamp;
++ if (SkShader::kRepeat_TileMode == m)
++ return int_repeat;
++ SkASSERT(SkShader::kMirror_TileMode == m);
++ return int_mirror;
++}
++
+ SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc()
+ {
+ int index = 0;
+ if (fDoFilter)
+ index = 1;
+ if (fInvType & SkMatrix::kPerspective_Mask)
++ index |= 6;
++ else if (fInvType & SkMatrix::kAffine_Mask)
+ index |= 4;
+- else if (fInvType & SkMatrix::kAffine_Mask)
++ else if (fInvType & SkMatrix::kScale_Mask)
+ index |= 2;
+
+ if (SkShader::kClamp_TileMode == fTileModeX &&
+@@ -123,6 +170,8 @@
+ // only general needs these procs
+ fTileProcX = choose_tile_proc(fTileModeX);
+ fTileProcY = choose_tile_proc(fTileModeY);
++ iTileProcX = choose_int_tile_proc(fTileModeX);
++ iTileProcY = choose_int_tile_proc(fTileModeY);
+ return GeneralXY_Procs[index];
+ }
+