summaryrefslogtreecommitdiff
path: root/skia
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2012-11-14 11:43:16 +0000
committerTorne (Richard Coles) <torne@google.com>2012-11-14 11:43:16 +0000
commit5821806d5e7f356e8fa4b058a389a808ea183019 (patch)
treee19f4793aac92e2c0d9a01087019a60d6657d838 /skia
parent8e79a8efe247f109aafd917a69e8a392961b3687 (diff)
downloadchromium_org-5821806d5e7f356e8fa4b058a389a808ea183019.tar.gz
Merge from Chromium at DEPS revision r167172
This commit was generated by merge_to_master.py. Change-Id: Ib8d56fd5ae39a2d7e8c91dcd76cc6d13f25f2aab
Diffstat (limited to 'skia')
-rw-r--r--skia/OWNERS6
-rw-r--r--skia/README.chromium24
-rw-r--r--skia/config/SkUserConfig.h252
-rw-r--r--skia/ext/SkFontHost_fontconfig.cpp369
-rw-r--r--skia/ext/SkFontHost_fontconfig_control.h38
-rw-r--r--skia/ext/SkFontHost_fontconfig_direct.cpp478
-rw-r--r--skia/ext/SkFontHost_fontconfig_direct.h59
-rw-r--r--skia/ext/SkFontHost_fontconfig_impl.h73
-rw-r--r--skia/ext/SkMemory_new_handler.cpp77
-rw-r--r--skia/ext/SkThread_chrome.cc88
-rw-r--r--skia/ext/SkTypeface_fake.cpp21
-rw-r--r--skia/ext/bitmap_platform_device.h36
-rw-r--r--skia/ext/bitmap_platform_device_android.cc89
-rw-r--r--skia/ext/bitmap_platform_device_android.h60
-rw-r--r--skia/ext/bitmap_platform_device_data.h102
-rw-r--r--skia/ext/bitmap_platform_device_linux.cc203
-rw-r--r--skia/ext/bitmap_platform_device_linux.h114
-rw-r--r--skia/ext/bitmap_platform_device_mac.cc282
-rw-r--r--skia/ext/bitmap_platform_device_mac.h88
-rw-r--r--skia/ext/bitmap_platform_device_mac_unittest.cc69
-rw-r--r--skia/ext/bitmap_platform_device_win.cc299
-rw-r--r--skia/ext/bitmap_platform_device_win.h99
-rw-r--r--skia/ext/convolver.cc860
-rw-r--r--skia/ext/convolver.h173
-rw-r--r--skia/ext/convolver_unittest.cc321
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/00_pc_clean.pngbin0 -> 289 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/00_vc_clean.pngbin0 -> 289 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/01_pc_drawargb.pngbin0 -> 289 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/01_vc_drawargb.pngbin0 -> 289 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.pngbin0 -> 537 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.pngbin0 -> 537 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.pngbin0 -> 417 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.pngbin0 -> 417 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.pngbin0 -> 417 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.pngbin0 -> 417 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.pngbin0 -> 433 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.pngbin0 -> 433 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.pngbin0 -> 109 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.pngbin0 -> 109 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.pngbin0 -> 126 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.pngbin0 -> 126 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.pngbin0 -> 271 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.pngbin0 -> 271 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/bitmaps/00_pc_opaque.pngbin0 -> 5140 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/bitmaps/00_vc_opaque.pngbin0 -> 5140 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/bitmaps/01_pc_alpha.pngbin0 -> 2699 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/bitmaps/01_vc_alpha.pngbin0 -> 2699 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/bitmaps/bitmap_alpha.pngbin0 -> 422 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/bitmaps/bitmap_opaque.pngbin0 -> 3287 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/00_pc_circle_stroke.pngbin0 -> 383 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/00_vc_circle_stroke.pngbin0 -> 410 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/01_pc_circle_fill.pngbin0 -> 463 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/01_vc_circle_fill.pngbin0 -> 481 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/02_pc_circle_over_strike.pngbin0 -> 477 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/02_vc_circle_over_strike.pngbin0 -> 513 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.pngbin0 -> 566 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.pngbin0 -> 602 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/04_pc_mixed_stroke.pngbin0 -> 1180 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/circles/04_vc_mixed_stroke.pngbin0 -> 1228 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingclean/00_pc_clipped.pngbin0 -> 1354 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingclean/00_vc_clipped.pngbin0 -> 1354 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingclean/01_pc_unclipped.pngbin0 -> 4683 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingclean/01_vc_unclipped.pngbin0 -> 4683 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingcombined/00_pc_combined.pngbin0 -> 1354 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingcombined/00_vc_combined.pngbin0 -> 1354 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingintersect/00_pc_intersect.pngbin0 -> 1211 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingintersect/00_vc_intersect.pngbin0 -> 1211 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingpath/00_pc_path.pngbin0 -> 1132 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingpath/00_vc_path.pngbin0 -> 1132 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingrect/00_pc_rect.pngbin0 -> 1459 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/clippingrect/00_vc_rect.pngbin0 -> 1459 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/diagonallines/00_pc_nw-se.pngbin0 -> 536 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/diagonallines/00_vc_nw-se.pngbin0 -> 536 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/diagonallines/01_pc_sw-ne.pngbin0 -> 735 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/diagonallines/01_vc_sw-ne.pngbin0 -> 737 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/diagonallines/02_pc_ne-sw.pngbin0 -> 756 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/diagonallines/02_vc_ne-sw.pngbin0 -> 760 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/diagonallines/03_pc_se-nw.pngbin0 -> 765 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/diagonallines/03_vc_se-nw.pngbin0 -> 781 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/lineorientation/00_pc_horizontal.pngbin0 -> 313 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/lineorientation/00_vc_horizontal.pngbin0 -> 319 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/lineorientation/01_pc_vertical.pngbin0 -> 328 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/lineorientation/01_vc_vertical.pngbin0 -> 344 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.pngbin0 -> 333 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.pngbin0 -> 348 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/lineorientation/03_pc_vertical_180.pngbin0 -> 332 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/lineorientation/03_vc_vertical_180.pngbin0 -> 351 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/matrix/00_pc_translate1.pngbin0 -> 5139 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/matrix/00_vc_translate1.pngbin0 -> 5139 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/matrix/01_pc_translate2.pngbin0 -> 4645 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/matrix/01_vc_translate2.pngbin0 -> 4645 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/matrix/02_pc_scale.pngbin0 -> 6566 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/matrix/02_vc_scale.pngbin0 -> 12292 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/matrix/03_pc_rotate.pngbin0 -> 9749 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/matrix/03_vc_rotate.pngbin0 -> 13795 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/patheffects/00_pc_dash_line.pngbin0 -> 299 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/patheffects/00_vc_dash_line.pngbin0 -> 299 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/patheffects/01_pc_dash_path.pngbin0 -> 348 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/patheffects/01_vc_dash_path.pngbin0 -> 343 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/patheffects/02_pc_dash_rect.pngbin0 -> 387 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/patheffects/02_vc_dash_rect.pngbin0 -> 395 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/patheffects/03_pc_circle.pngbin0 -> 497 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/patheffects/03_vc_circle.pngbin0 -> 519 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.pngbin0 -> 307 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.pngbin0 -> 307 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.pngbin0 -> 313 bytes
-rw-r--r--skia/ext/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.pngbin0 -> 319 bytes
-rw-r--r--skia/ext/google_logging.cc25
-rw-r--r--skia/ext/image_operations.cc541
-rw-r--r--skia/ext/image_operations.h129
-rw-r--r--skia/ext/image_operations_bench.cc293
-rw-r--r--skia/ext/image_operations_unittest.cc634
-rw-r--r--skia/ext/platform_canvas.cc92
-rw-r--r--skia/ext/platform_canvas.h180
-rw-r--r--skia/ext/platform_canvas_linux.cc38
-rw-r--r--skia/ext/platform_canvas_mac.cc56
-rw-r--r--skia/ext/platform_canvas_skia.cc113
-rw-r--r--skia/ext/platform_canvas_unittest.cc398
-rw-r--r--skia/ext/platform_canvas_win.cc40
-rw-r--r--skia/ext/platform_device.cc79
-rw-r--r--skia/ext/platform_device.h173
-rw-r--r--skia/ext/platform_device_linux.cc17
-rw-r--r--skia/ext/platform_device_mac.cc155
-rw-r--r--skia/ext/platform_device_win.cc237
-rw-r--r--skia/ext/skia_sandbox_support_win.cc24
-rw-r--r--skia/ext/skia_sandbox_support_win.h16
-rw-r--r--skia/ext/skia_trace_shim.h17
-rw-r--r--skia/ext/skia_utils_ios.h33
-rw-r--r--skia/ext/skia_utils_ios.mm72
-rw-r--r--skia/ext/skia_utils_mac.h120
-rw-r--r--skia/ext/skia_utils_mac.mm418
-rw-r--r--skia/ext/skia_utils_mac_unittest.mm244
-rw-r--r--skia/ext/skia_utils_win.cc70
-rw-r--r--skia/ext/skia_utils_win.h49
-rw-r--r--skia/ext/vector_canvas.cc38
-rw-r--r--skia/ext/vector_canvas.h40
-rw-r--r--skia/ext/vector_canvas_unittest.cc968
-rw-r--r--skia/ext/vector_platform_device_emf_win.cc874
-rw-r--r--skia/ext/vector_platform_device_emf_win.h132
-rw-r--r--skia/ext/vector_platform_device_skia.cc89
-rw-r--r--skia/ext/vector_platform_device_skia.h60
-rw-r--r--skia/fix_for_1186198.diff38
-rw-r--r--skia/skia.gyp853
-rw-r--r--skia/skia.target.mk490
-rw-r--r--skia/skia_Prefix.pch12
-rw-r--r--skia/skia_opts.target.mk161
-rw-r--r--skia/skia_test_expectations.txt72
-rw-r--r--skia/tile_patch.diff284
148 files changed, 12654 insertions, 0 deletions
diff --git a/skia/OWNERS b/skia/OWNERS
new file mode 100644
index 0000000000..e4b8e1d2b4
--- /dev/null
+++ b/skia/OWNERS
@@ -0,0 +1,6 @@
+alokp@chromium.org
+reed@google.com
+vandebo@chromium.org
+senorblanco@chromium.org
+thakis@chromium.org
+twiz@chromium.org
diff --git a/skia/README.chromium b/skia/README.chromium
new file mode 100644
index 0000000000..76a92840d7
--- /dev/null
+++ b/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/skia/config/SkUserConfig.h b/skia/config/SkUserConfig.h
new file mode 100644
index 0000000000..0b3f3c7f73
--- /dev/null
+++ b/skia/config/SkUserConfig.h
@@ -0,0 +1,252 @@
+/*
+ * 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>
+#if defined(USE_SYSTEM_ZLIB)
+#define SK_ZLIB_INCLUDE <zlib.h>
+#else
+#define SK_ZLIB_INCLUDE "third_party/zlib/zlib.h"
+#endif
+
+/* 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
+
+/* If this is not defined, skia dithers gradients. Turning this on will make
+ gradients look better, but might have a performance impact. When it's turned
+ on, several webkit pixel tests will need to be rebaselined, too.
+ http://crbug.com/41756
+ */
+#define SK_DISABLE_DITHER_32BIT_GRADIENT
+
+// ===== Begin Chrome-specific definitions =====
+
+#define SK_SCALAR_IS_FLOAT
+#undef SK_SCALAR_IS_FIXED
+
+#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/skia/ext/SkFontHost_fontconfig.cpp b/skia/ext/SkFontHost_fontconfig.cpp
new file mode 100644
index 0000000000..757672fe1f
--- /dev/null
+++ b/skia/ext/SkFontHost_fontconfig.cpp
@@ -0,0 +1,369 @@
+/* libs/graphics/ports/SkFontHost_fontconfig.cpp
+**
+** Copyright 2008, Google Inc.
+**
+** 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.
+*/
+
+// -----------------------------------------------------------------------------
+// This file provides implementations of the font resolution members of
+// SkFontHost by using the fontconfig[1] library. Fontconfig is usually found
+// on Linux systems and handles configuration, parsing and caching issues
+// involved with enumerating and matching fonts.
+//
+// [1] http://fontconfig.org
+// -----------------------------------------------------------------------------
+
+#include <map>
+#include <string>
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/compiler_specific.h"
+#include "third_party/skia/src/ports/SkFontDescriptor.h"
+#include "SkFontHost.h"
+#include "SkStream.h"
+#include "SkFontHost_fontconfig_control.h"
+#include "SkFontHost_fontconfig_impl.h"
+#include "SkFontHost_fontconfig_direct.h"
+
+static FontConfigInterface* global_fc_impl = NULL;
+
+void SkiaFontConfigUseDirectImplementation() {
+ if (global_fc_impl)
+ delete global_fc_impl;
+ global_fc_impl = new FontConfigDirect;
+}
+
+void SkiaFontConfigSetImplementation(FontConfigInterface* font_config) {
+ if (global_fc_impl)
+ delete global_fc_impl;
+ global_fc_impl = font_config;
+}
+
+static FontConfigInterface* GetFcImpl() {
+ if (!global_fc_impl)
+ global_fc_impl = new FontConfigDirect;
+ return global_fc_impl;
+}
+
+SK_DECLARE_STATIC_MUTEX(global_remote_font_map_lock);
+static std::map<uint32_t, std::pair<uint8_t*, size_t> >* global_remote_fonts;
+
+// Initialize the map declared above. Note that its corresponding mutex must be
+// locked before calling this function.
+static void AllocateGlobalRemoteFontsMapOnce() {
+ if (!global_remote_fonts) {
+ global_remote_fonts =
+ new std::map<uint32_t, std::pair<uint8_t*, size_t> >();
+ }
+}
+
+static unsigned global_next_remote_font_id;
+
+// This is the maximum size of the font cache.
+static const unsigned kFontCacheMemoryBudget = 2 * 1024 * 1024; // 2MB
+
+// UniqueIds are encoded as (filefaceid << 8) | style
+// For system fonts, filefaceid = (fileid << 4) | face_index.
+// For remote fonts, filefaceid = fileid.
+
+static unsigned UniqueIdToFileFaceId(unsigned uniqueid)
+{
+ return uniqueid >> 8;
+}
+
+static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid)
+{
+ return static_cast<SkTypeface::Style>(uniqueid & 0xff);
+}
+
+static unsigned FileFaceIdAndStyleToUniqueId(unsigned filefaceid,
+ SkTypeface::Style style)
+{
+ SkASSERT((style & 0xff) == style);
+ return (filefaceid << 8) | static_cast<int>(style);
+}
+
+static const unsigned kRemoteFontMask = 0x00800000u;
+
+static bool IsRemoteFont(unsigned filefaceid)
+{
+ return filefaceid & kRemoteFontMask;
+}
+
+class FontConfigTypeface : public SkTypeface {
+public:
+ FontConfigTypeface(Style style, uint32_t id)
+ : SkTypeface(style, id)
+ { }
+
+ virtual ~FontConfigTypeface()
+ {
+ const uint32_t id = uniqueID();
+ if (IsRemoteFont(UniqueIdToFileFaceId(id))) {
+ SkAutoMutexAcquire ac(global_remote_font_map_lock);
+ AllocateGlobalRemoteFontsMapOnce();
+ std::map<uint32_t, std::pair<uint8_t*, size_t> >::iterator iter
+ = global_remote_fonts->find(id);
+ if (iter != global_remote_fonts->end()) {
+ sk_free(iter->second.first); // remove the font on memory.
+ global_remote_fonts->erase(iter);
+ }
+ }
+ }
+};
+
+// static
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+ const char familyName[],
+ SkTypeface::Style style)
+{
+ std::string resolved_family_name;
+
+ if (familyFace) {
+ // Given the fileid we can ask fontconfig for the familyname of the
+ // font.
+ const unsigned filefaceid = UniqueIdToFileFaceId(familyFace->uniqueID());
+ if (!GetFcImpl()->Match(&resolved_family_name, NULL,
+ true /* filefaceid valid */, filefaceid, "",
+ NULL, 0, NULL, NULL)) {
+ return NULL;
+ }
+ } else if (familyName) {
+ resolved_family_name = familyName;
+ }
+
+ bool bold = style & SkTypeface::kBold;
+ bool italic = style & SkTypeface::kItalic;
+ unsigned filefaceid;
+ if (!GetFcImpl()->Match(NULL, &filefaceid,
+ false, -1, /* no filefaceid */
+ resolved_family_name, NULL, 0,
+ &bold, &italic)) {
+ return NULL;
+ }
+ const SkTypeface::Style resulting_style = static_cast<SkTypeface::Style>(
+ (bold ? SkTypeface::kBold : 0) |
+ (italic ? SkTypeface::kItalic : 0));
+
+ const unsigned id = FileFaceIdAndStyleToUniqueId(filefaceid,
+ resulting_style);
+ SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (resulting_style, id));
+ return typeface;
+}
+
+// static
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream)
+{
+ if (!stream)
+ return NULL;
+
+ const size_t length = stream->read(0, 0);
+ if (!length)
+ return NULL;
+ if (length >= 1024 * 1024 * 1024)
+ return NULL; // don't accept too large fonts (>= 1GB) for safety.
+
+ uint8_t* font = (uint8_t*)sk_malloc_throw(length);
+ if (stream->read(font, length) != length) {
+ sk_free(font);
+ return NULL;
+ }
+
+ SkTypeface::Style style = static_cast<SkTypeface::Style>(0);
+ unsigned id = 0;
+ {
+ SkAutoMutexAcquire ac(global_remote_font_map_lock);
+ AllocateGlobalRemoteFontsMapOnce();
+ id = FileFaceIdAndStyleToUniqueId(
+ global_next_remote_font_id | kRemoteFontMask, style);
+
+ if (++global_next_remote_font_id >= kRemoteFontMask)
+ global_next_remote_font_id = 0;
+
+ if (!global_remote_fonts->insert(
+ std::make_pair(id, std::make_pair(font, length))).second) {
+ sk_free(font);
+ return NULL;
+ }
+ }
+
+ SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id));
+ return typeface;
+}
+
+// static
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
+{
+ SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented");
+ return NULL;
+}
+
+uint32_t SkFontHost::NextLogicalFont(SkFontID curr, SkFontID orig) {
+ // We don't handle font fallback, WebKit does.
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Serialize, Deserialize need to be compatible across platforms, hence the use
+// of SkFontDescriptor.
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+ SkFontDescriptor desc(face->style());
+
+ std::string resolved_family_name;
+
+ const unsigned filefaceid = UniqueIdToFileFaceId(face->uniqueID());
+ if (GetFcImpl()->Match(&resolved_family_name, NULL,
+ true /* filefaceid valid */, filefaceid, "", NULL, 0, NULL, NULL))
+ desc.setFamilyName(resolved_family_name.c_str());
+ else
+ desc.setFamilyName("sans-serif");
+
+ // would also like other names (see SkFontDescriptor.h)
+
+ desc.serialize(stream);
+
+ // by convention, we also write out the actual sfnt data, preceeded by
+ // a packed-length. For now we skip that, so we just write the zero.
+ stream->writePackedUInt(0);
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+ SkFontDescriptor desc(stream);
+
+ // by convention, Serialize will have also written the actual sfnt data.
+ // for now, we just want to skip it.
+ size_t size = stream->readPackedUInt();
+ stream->skip(size);
+
+ return SkFontHost::CreateTypeface(NULL, desc.getFamilyName(),
+ desc.getStyle());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkFileDescriptorStream : public SkStream {
+ public:
+ SkFileDescriptorStream(int fd) {
+ memory_ = NULL;
+ offset_ = 0;
+
+ // this ensures that if we fail in the constructor, we will safely
+ // ignore all subsequent calls to read() because we will always trim
+ // the requested size down to 0
+ length_ = 0;
+
+ struct stat st;
+ if (fstat(fd, &st))
+ return;
+
+ void* memory = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+ if (memory == MAP_FAILED)
+ return;
+
+ memory_ = reinterpret_cast<uint8_t*>(memory);
+ length_ = st.st_size;
+ }
+
+ virtual ~SkFileDescriptorStream() {
+ munmap(const_cast<uint8_t*>(memory_), length_);
+ }
+
+ virtual bool rewind() OVERRIDE {
+ offset_ = 0;
+ return true;
+ }
+
+ // SkStream implementation.
+ virtual size_t read(void* buffer, size_t size) OVERRIDE {
+ if (!buffer && !size) {
+ // This is request for the length of the stream.
+ return length_;
+ }
+
+ size_t remaining = length_ - offset_;
+ if (size > remaining)
+ size = remaining;
+ if (buffer)
+ memcpy(buffer, memory_ + offset_, size);
+
+ offset_ += size;
+ return size;
+ }
+
+ virtual const void* getMemoryBase() OVERRIDE {
+ return memory_;
+ }
+
+ private:
+ const uint8_t* memory_;
+ size_t offset_, length_;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// static
+SkStream* SkFontHost::OpenStream(uint32_t id)
+{
+ const unsigned filefaceid = UniqueIdToFileFaceId(id);
+
+ if (IsRemoteFont(filefaceid)) {
+ // remote font
+ SkAutoMutexAcquire ac(global_remote_font_map_lock);
+ AllocateGlobalRemoteFontsMapOnce();
+ std::map<uint32_t, std::pair<uint8_t*, size_t> >::const_iterator iter
+ = global_remote_fonts->find(id);
+ if (iter == global_remote_fonts->end())
+ return NULL;
+ return SkNEW_ARGS(
+ SkMemoryStream, (iter->second.first, iter->second.second));
+ }
+
+ // system font
+ const int fd = GetFcImpl()->Open(filefaceid);
+ if (fd < 0)
+ return NULL;
+
+ return SkNEW_ARGS(SkFileDescriptorStream, (fd));
+}
+
+// static
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
+ int32_t* index) {
+ const unsigned filefaceid = UniqueIdToFileFaceId(fontID);
+
+ if (IsRemoteFont(filefaceid))
+ return 0;
+
+ if (index) {
+ *index = filefaceid & 0xfu;
+ // 1 is a bogus return value.
+ // We had better change the signature of this function in Skia
+ // to return bool to indicate success/failure and have another
+ // out param for fileName length.
+ if (!path)
+ return 1;
+ }
+
+ if (path)
+ SkASSERT(!"SkFontHost::GetFileName does not support the font path "
+ "retrieval.");
+
+ return 0;
+}
diff --git a/skia/ext/SkFontHost_fontconfig_control.h b/skia/ext/SkFontHost_fontconfig_control.h
new file mode 100644
index 0000000000..336634b012
--- /dev/null
+++ b/skia/ext/SkFontHost_fontconfig_control.h
@@ -0,0 +1,38 @@
+/* libs/graphics/ports/SkFontHost_fontconfig_control.h
+**
+** Copyright 2009, Google Inc.
+**
+** 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 FontConfigControl_DEFINED
+#define FontConfigControl_DEFINED
+
+#include "SkPreConfig.h"
+
+class FontConfigInterface;
+
+// Sets the Skia FontHost to use the direct (non-IPC, requires filesystem
+// access) FontConfig implementation. Any previously-set FontConfigInterface
+// will be freed.
+SK_API void SkiaFontConfigUseDirectImplementation();
+
+// Sets the Skia FontHost to use the given implementation of FontConfig. This
+// is normally used to configure the IPC-based implementation to get out of
+// the sandbox.
+//
+// Ownership of the given pointer is transferred, and any previously-set
+// FontConfigInterface will be freed.
+SK_API void SkiaFontConfigSetImplementation(FontConfigInterface* font_config);
+
+#endif // FontConfigControl_DEFINED
diff --git a/skia/ext/SkFontHost_fontconfig_direct.cpp b/skia/ext/SkFontHost_fontconfig_direct.cpp
new file mode 100644
index 0000000000..fd0e24353a
--- /dev/null
+++ b/skia/ext/SkFontHost_fontconfig_direct.cpp
@@ -0,0 +1,478 @@
+/* libs/graphics/ports/SkFontHost_fontconfig_direct.cpp
+**
+** Copyright 2009, Google Inc.
+**
+** 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.
+*/
+
+#include "skia/ext/SkFontHost_fontconfig_direct.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <fontconfig/fontconfig.h>
+
+#include "third_party/skia/include/core/SkTypeface.h"
+#include "third_party/skia/include/core/SkUtils.h"
+
+namespace {
+
+// Equivalence classes, used to match the Liberation and other fonts
+// with their metric-compatible replacements. See the discussion in
+// GetFontEquivClass().
+enum FontEquivClass
+{
+ OTHER,
+ SANS,
+ SERIF,
+ MONO,
+ SYMBOL,
+ PGOTHIC,
+ GOTHIC,
+ PMINCHO,
+ MINCHO,
+ SIMSUN,
+ NSIMSUN,
+ SIMHEI,
+};
+
+// Match the font name against a whilelist of fonts, returning the equivalence
+// class.
+FontEquivClass GetFontEquivClass(const char* fontname)
+{
+ // It would be nice for fontconfig to tell us whether a given suggested
+ // replacement is a "strong" match (that is, an equivalent font) or
+ // a "weak" match (that is, fontconfig's next-best attempt at finding a
+ // substitute). However, I played around with the fontconfig API for
+ // a good few hours and could not make it reveal this information.
+ //
+ // So instead, we hardcode. Initially this function emulated
+ // /etc/fonts/conf.d/30-metric-aliases.conf
+ // from my Ubuntu system, but we're better off being very conservative.
+
+ // Arimo, Tinos and Cousine are a set of fonts metric-compatible with
+ // Arial, Times New Roman and Courier New with a character repertoire
+ // much larger than Liberation. Note that Cousine is metrically
+ // compatible with Courier New, but the former is sans-serif while
+ // the latter is serif.
+
+
+ struct FontEquivMap {
+ FontEquivClass clazz;
+ const char name[40];
+ };
+
+ static const FontEquivMap kFontEquivMap[] = {
+ { SANS, "Arial" },
+ { SANS, "Arimo" },
+ { SANS, "Liberation Sans" },
+
+ { SERIF, "Times New Roman" },
+ { SERIF, "Tinos" },
+ { SERIF, "Liberation Serif" },
+
+ { MONO, "Courier New" },
+ { MONO, "Cousine" },
+ { MONO, "Liberation Mono" },
+
+ { SYMBOL, "Symbol" },
+ { SYMBOL, "Symbol Neu" },
+
+ // MS Pゴシック
+ { PGOTHIC, "MS PGothic" },
+ { PGOTHIC, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
+ "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
+ { PGOTHIC, "IPAPGothic" },
+ { PGOTHIC, "MotoyaG04Gothic" },
+
+ // MS ゴシック
+ { GOTHIC, "MS Gothic" },
+ { GOTHIC, "\xef\xbc\xad\xef\xbc\xb3 "
+ "\xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf" },
+ { GOTHIC, "IPAGothic" },
+ { GOTHIC, "MotoyaG04GothicMono" },
+
+ // MS P明朝
+ { PMINCHO, "MS PMincho" },
+ { PMINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xef\xbc\xb0"
+ "\xe6\x98\x8e\xe6\x9c\x9d"},
+ { PMINCHO, "IPAPMincho" },
+ { PMINCHO, "MotoyaG04Mincho" },
+
+ // MS 明朝
+ { MINCHO, "MS Mincho" },
+ { MINCHO, "\xef\xbc\xad\xef\xbc\xb3 \xe6\x98\x8e\xe6\x9c\x9d" },
+ { MINCHO, "IPAMincho" },
+ { MINCHO, "MotoyaG04MinchoMono" },
+
+ // 宋体
+ { SIMSUN, "Simsun" },
+ { SIMSUN, "\xe5\xae\x8b\xe4\xbd\x93" },
+ { SIMSUN, "Song ASC" },
+
+ // 新宋体
+ { NSIMSUN, "NSimsun" },
+ { NSIMSUN, "\xe6\x96\xb0\xe5\xae\x8b\xe4\xbd\x93" },
+ { NSIMSUN, "N Song ASC" },
+
+ // 黑体
+ { SIMHEI, "Simhei" },
+ { SIMHEI, "\xe9\xbb\x91\xe4\xbd\x93" },
+ { SIMHEI, "MYingHeiGB18030" },
+ { SIMHEI, "MYingHeiB5HK" },
+ };
+
+ static const size_t kFontCount =
+ sizeof(kFontEquivMap)/sizeof(kFontEquivMap[0]);
+
+ // TODO(jungshik): If this loop turns out to be hot, turn
+ // the array to a static (hash)map to speed it up.
+ for (size_t i = 0; i < kFontCount; ++i) {
+ if (strcasecmp(kFontEquivMap[i].name, fontname) == 0)
+ return kFontEquivMap[i].clazz;
+ }
+ return OTHER;
+}
+
+
+// Return true if |font_a| and |font_b| are visually and at the metrics
+// level interchangeable.
+bool IsMetricCompatibleReplacement(const char* font_a, const char* font_b)
+{
+ FontEquivClass class_a = GetFontEquivClass(font_a);
+ FontEquivClass class_b = GetFontEquivClass(font_b);
+
+ return class_a != OTHER && class_a == class_b;
+}
+
+inline unsigned FileFaceIdToFileId(unsigned filefaceid)
+{
+ return filefaceid >> 4;
+}
+
+inline unsigned FileIdAndFaceIndexToFileFaceId(unsigned fileid, int face_index)
+{
+ SkASSERT((face_index & 0xfu) == face_index);
+ return (fileid << 4) | face_index;
+}
+
+// Normally we only return exactly the font asked for. In last-resort
+// cases, the request either doesn't specify a font or is one of the
+// basic font names like "Sans", "Serif" or "Monospace". This function
+// tells you whether a given request is for such a fallback.
+bool IsFallbackFontAllowed(const std::string& family) {
+ const char* family_cstr = family.c_str();
+ return family.empty() ||
+ strcasecmp(family_cstr, "sans") == 0 ||
+ strcasecmp(family_cstr, "serif") == 0 ||
+ strcasecmp(family_cstr, "monospace") == 0;
+}
+
+// Find matching font from |font_set| for the given font family.
+FcPattern* MatchFont(FcFontSet* font_set,
+ FcChar8* post_config_family,
+ const std::string& family) {
+ // Older versions of fontconfig have a bug where they cannot select
+ // only scalable fonts so we have to manually filter the results.
+ FcPattern* match = NULL;
+ for (int i = 0; i < font_set->nfont; ++i) {
+ FcPattern* current = font_set->fonts[i];
+ FcBool is_scalable;
+
+ if (FcPatternGetBool(current, FC_SCALABLE, 0,
+ &is_scalable) != FcResultMatch ||
+ !is_scalable) {
+ continue;
+ }
+
+ // fontconfig can also return fonts which are unreadable
+ FcChar8* c_filename;
+ if (FcPatternGetString(current, FC_FILE, 0, &c_filename) != FcResultMatch)
+ continue;
+
+ if (access(reinterpret_cast<char*>(c_filename), R_OK) != 0)
+ continue;
+
+ match = current;
+ break;
+ }
+
+ if (match && !IsFallbackFontAllowed(family)) {
+ bool acceptable_substitute = false;
+ for (int id = 0; id < 255; ++id) {
+ FcChar8* post_match_family;
+ if (FcPatternGetString(match, FC_FAMILY, id, &post_match_family) !=
+ FcResultMatch)
+ break;
+ acceptable_substitute =
+ (strcasecmp(reinterpret_cast<char*>(post_config_family),
+ reinterpret_cast<char*>(post_match_family)) == 0 ||
+ // Workaround for Issue 12530:
+ // requested family: "Bitstream Vera Sans"
+ // post_config_family: "Arial"
+ // post_match_family: "Bitstream Vera Sans"
+ // -> We should treat this case as a good match.
+ strcasecmp(family.c_str(),
+ reinterpret_cast<char*>(post_match_family)) == 0) ||
+ IsMetricCompatibleReplacement(family.c_str(),
+ reinterpret_cast<char*>(post_match_family));
+ if (acceptable_substitute)
+ break;
+ }
+ if (!acceptable_substitute)
+ return NULL;
+ }
+
+ return match;
+}
+
+// Retrieves |is_bold|, |is_italic| and |font_family| properties from |font|.
+bool GetFontProperties(FcPattern* font,
+ std::string* font_family,
+ bool* is_bold,
+ bool* is_italic) {
+ FcChar8* c_family;
+ if (FcPatternGetString(font, FC_FAMILY, 0, &c_family))
+ return false;
+
+ int resulting_bold;
+ if (FcPatternGetInteger(font, FC_WEIGHT, 0, &resulting_bold))
+ resulting_bold = FC_WEIGHT_NORMAL;
+
+ int resulting_italic;
+ if (FcPatternGetInteger(font, FC_SLANT, 0, &resulting_italic))
+ resulting_italic = FC_SLANT_ROMAN;
+
+ // If we ask for an italic font, fontconfig might take a roman font and set
+ // the undocumented property FC_MATRIX to a skew matrix. It'll then say
+ // that the font is italic or oblique. So, if we see a matrix, we don't
+ // believe that it's italic.
+ FcValue matrix;
+ const bool have_matrix = FcPatternGet(font, FC_MATRIX, 0, &matrix) == 0;
+
+ // If we ask for an italic font, fontconfig might take a roman font and set
+ // FC_EMBOLDEN.
+ FcValue embolden;
+ const bool have_embolden =
+ FcPatternGet(font, FC_EMBOLDEN, 0, &embolden) == 0;
+
+ *is_bold = resulting_bold > FC_WEIGHT_MEDIUM && !have_embolden;
+ *is_italic = resulting_italic > FC_SLANT_ROMAN && !have_matrix;
+ *font_family = reinterpret_cast<char*>(c_family);
+
+ return true;
+}
+
+} // anonymous namespace
+
+FontConfigDirect::FontConfigDirect()
+ : next_file_id_(0) {
+ FcInit();
+}
+
+FontConfigDirect::~FontConfigDirect() {
+}
+
+bool FontConfigDirect::Match(std::string* result_family,
+ unsigned* result_filefaceid,
+ bool filefaceid_valid, unsigned filefaceid,
+ const std::string& family,
+ const void* data, size_t characters_bytes,
+ bool* is_bold, bool* is_italic) {
+ if (family.length() > kMaxFontFamilyLength)
+ return false;
+
+ SkAutoMutexAcquire ac(mutex_);
+
+ // Given |family|, |is_bold| and |is_italic| but not |data|, the result will
+ // be a function of these three parameters, and thus eligible for caching.
+ // This is the fast path for |SkTypeface::CreateFromName()|.
+ bool eligible_for_cache = !family.empty() && is_bold && is_italic && !data;
+ if (eligible_for_cache) {
+ int style = (*is_bold ? SkTypeface::kBold : 0 ) |
+ (*is_italic ? SkTypeface::kItalic : 0);
+ FontMatchKey key = FontMatchKey(family, style);
+ const std::map<FontMatchKey, FontMatch>::const_iterator i =
+ font_match_cache_.find(key);
+ if (i != font_match_cache_.end()) {
+ *is_bold = i->second.is_bold;
+ *is_italic = i->second.is_italic;
+ if (result_family)
+ *result_family = i->second.family;
+ if (result_filefaceid)
+ *result_filefaceid = i->second.filefaceid;
+ return true;
+ }
+ }
+
+ FcPattern* pattern = FcPatternCreate();
+
+ if (filefaceid_valid) {
+ const std::map<unsigned, std::string>::const_iterator
+ i = fileid_to_filename_.find(FileFaceIdToFileId(filefaceid));
+ if (i == fileid_to_filename_.end()) {
+ FcPatternDestroy(pattern);
+ return false;
+ }
+ int face_index = filefaceid & 0xfu;
+ FcPatternAddString(pattern, FC_FILE,
+ reinterpret_cast<const FcChar8*>(i->second.c_str()));
+ // face_index is added only when family is empty because it is not
+ // necessary to uniquiely identify a font if both file and
+ // family are given.
+ if (family.empty())
+ FcPatternAddInteger(pattern, FC_INDEX, face_index);
+ }
+ if (!family.empty()) {
+ FcPatternAddString(pattern, FC_FAMILY, (FcChar8*) family.c_str());
+ }
+
+ FcCharSet* charset = NULL;
+ if (data) {
+ charset = FcCharSetCreate();
+ const uint16_t* chars = (const uint16_t*)data;
+ const uint16_t* stop = chars + characters_bytes/2;
+ while (chars < stop) {
+ FcCharSetAddChar(charset, SkUTF16_NextUnichar(&chars));
+ }
+ FcPatternAddCharSet(pattern, FC_CHARSET, charset);
+ FcCharSetDestroy(charset); // pattern now owns it.
+ }
+
+ FcPatternAddInteger(pattern, FC_WEIGHT,
+ is_bold && *is_bold ? FC_WEIGHT_BOLD
+ : FC_WEIGHT_NORMAL);
+ FcPatternAddInteger(pattern, FC_SLANT,
+ is_italic && *is_italic ? FC_SLANT_ITALIC
+ : FC_SLANT_ROMAN);
+ FcPatternAddBool(pattern, FC_SCALABLE, FcTrue);
+
+ FcConfigSubstitute(NULL, pattern, FcMatchPattern);
+ FcDefaultSubstitute(pattern);
+
+ // Font matching:
+ // CSS often specifies a fallback list of families:
+ // font-family: a, b, c, serif;
+ // However, fontconfig will always do its best to find *a* font when asked
+ // for something so we need a way to tell if the match which it has found is
+ // "good enough" for us. Otherwise, we can return NULL which gets piped up
+ // and lets WebKit know to try the next CSS family name. However, fontconfig
+ // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
+ // wish to support that.
+ //
+ // Thus, if a specific family is requested we set @family_requested. Then we
+ // record two strings: the family name after config processing and the
+ // family name after resolving. If the two are equal, it's a good match.
+ //
+ // So consider the case where a user has mapped Arial to Helvetica in their
+ // config.
+ // requested family: "Arial"
+ // post_config_family: "Helvetica"
+ // post_match_family: "Helvetica"
+ // -> good match
+ //
+ // and for a missing font:
+ // requested family: "Monaco"
+ // post_config_family: "Monaco"
+ // post_match_family: "Times New Roman"
+ // -> BAD match
+ //
+ // However, we special-case fallback fonts; see IsFallbackFontAllowed().
+ FcChar8* post_config_family;
+ FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);
+
+ FcResult result;
+ FcFontSet* font_set = FcFontSort(0, pattern, 0, 0, &result);
+ if (!font_set) {
+ FcPatternDestroy(pattern);
+ return false;
+ }
+
+ FcPattern* match = MatchFont(font_set, post_config_family, family);
+ if (!match) {
+ FcPatternDestroy(pattern);
+ FcFontSetDestroy(font_set);
+ return false;
+ }
+
+ FcPatternDestroy(pattern);
+
+ FcChar8* c_filename;
+ if (FcPatternGetString(match, FC_FILE, 0, &c_filename) != FcResultMatch) {
+ FcFontSetDestroy(font_set);
+ return false;
+ }
+ int face_index;
+ if (FcPatternGetInteger(match, FC_INDEX, 0, &face_index) != FcResultMatch) {
+ FcFontSetDestroy(font_set);
+ return false;
+ }
+
+ FontMatch font_match;
+ if (filefaceid_valid) {
+ font_match.filefaceid = filefaceid;
+ } else {
+ unsigned out_fileid;
+ const std::string filename(reinterpret_cast<char*>(c_filename));
+ const std::map<std::string, unsigned>::const_iterator
+ i = filename_to_fileid_.find(filename);
+ if (i == filename_to_fileid_.end()) {
+ out_fileid = next_file_id_++;
+ filename_to_fileid_[filename] = out_fileid;
+ fileid_to_filename_[out_fileid] = filename;
+ } else {
+ out_fileid = i->second;
+ }
+ // fileid stored in filename_to_fileid_ and fileid_to_filename_ is
+ // unique only up to the font file. We have to encode face_index for
+ // the out param.
+ font_match.filefaceid =
+ FileIdAndFaceIndexToFileFaceId(out_fileid, face_index);
+ }
+
+ bool success = GetFontProperties(match,
+ &font_match.family,
+ &font_match.is_bold,
+ &font_match.is_italic);
+ FcFontSetDestroy(font_set);
+
+ if (success) {
+ // If eligible, cache the result of the matching.
+ if (eligible_for_cache) {
+ int style = (*is_bold ? SkTypeface::kBold : 0 ) |
+ (*is_italic ? SkTypeface::kItalic : 0);
+ font_match_cache_[FontMatchKey(family, style)] = font_match;
+ }
+
+ if (result_family)
+ *result_family = font_match.family;
+ if (result_filefaceid)
+ *result_filefaceid = font_match.filefaceid;
+ if (is_bold)
+ *is_bold = font_match.is_bold;
+ if (is_italic)
+ *is_italic = font_match.is_italic;
+ }
+
+ return success;
+}
+
+int FontConfigDirect::Open(unsigned filefaceid) {
+ SkAutoMutexAcquire ac(mutex_);
+ const std::map<unsigned, std::string>::const_iterator
+ i = fileid_to_filename_.find(FileFaceIdToFileId(filefaceid));
+ if (i == fileid_to_filename_.end())
+ return -1;
+
+ return open(i->second.c_str(), O_RDONLY);
+}
diff --git a/skia/ext/SkFontHost_fontconfig_direct.h b/skia/ext/SkFontHost_fontconfig_direct.h
new file mode 100644
index 0000000000..ae97f3d105
--- /dev/null
+++ b/skia/ext/SkFontHost_fontconfig_direct.h
@@ -0,0 +1,59 @@
+/* libs/graphics/ports/SkFontHost_fontconfig_direct.h
+**
+** Copyright 2009, Google Inc.
+**
+** 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 FontConfigDirect_DEFINED
+#define FontConfigDirect_DEFINED
+
+#include <map>
+#include <string>
+
+#include "SkThread.h"
+#include "SkFontHost_fontconfig_impl.h"
+
+class SK_API FontConfigDirect : public FontConfigInterface {
+ public:
+ FontConfigDirect();
+ virtual ~FontConfigDirect();
+
+ // FontConfigInterface implementation. Thread safe.
+ virtual bool Match(std::string* result_family, unsigned* result_filefaceid,
+ bool filefaceid_valid, unsigned filefaceid,
+ const std::string& family,
+ const void* characters, size_t characters_bytes,
+ bool* is_bold, bool* is_italic) SK_OVERRIDE;
+ virtual int Open(unsigned filefaceid) SK_OVERRIDE;
+
+ private:
+ SkMutex mutex_;
+ // fileid stored in two maps below are unique per font file.
+ std::map<unsigned, std::string> fileid_to_filename_;
+ std::map<std::string, unsigned> filename_to_fileid_;
+
+ // Cache of |family,style| to |FontMatch| to minimize querying FontConfig.
+ typedef std::pair<std::string, int> FontMatchKey;
+ struct FontMatch {
+ std::string family;
+ bool is_bold;
+ bool is_italic;
+ unsigned filefaceid;
+ };
+ std::map<FontMatchKey, FontMatch> font_match_cache_;
+
+ unsigned next_file_id_;
+};
+
+#endif // FontConfigDirect_DEFINED
diff --git a/skia/ext/SkFontHost_fontconfig_impl.h b/skia/ext/SkFontHost_fontconfig_impl.h
new file mode 100644
index 0000000000..d6f8179640
--- /dev/null
+++ b/skia/ext/SkFontHost_fontconfig_impl.h
@@ -0,0 +1,73 @@
+/* libs/graphics/ports/SkFontHost_fontconfig_impl.h
+**
+** Copyright 2009, Google Inc.
+**
+** 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.
+*/
+
+/* The SkFontHost_fontconfig code requires an implementation of an abstact
+ * fontconfig interface. We do this because sometimes fontconfig is not
+ * directly availible and this provides an ability to change the fontconfig
+ * implementation at run-time.
+ */
+
+#ifndef FontConfigInterface_DEFINED
+#define FontConfigInterface_DEFINED
+
+#include <string>
+
+class FontConfigInterface {
+ public:
+ virtual ~FontConfigInterface() { }
+
+ /** Performs config match
+ *
+ * @param result_family (optional, set to NULL to ignore, output)
+ * on success, set to the resulting family name.
+ * @param result_filefaceid (optional, set to NULL to ignore, output)
+ * on success, set to the resulting fileface id.
+ * @param filefaceid_valid if true, then |filefaceid| is valid
+ * @param filefaceid the filefaceid (as returned by this function)
+ * which we are trying to match.
+ * @param family (optional) the family of the font that we are trying to
+ * match. If the length of the |family| is greater then
+ * kMaxFontFamilyLength, this function should immediately return false.
+ * @param characters (optional) UTF-16 characters the font must cover.
+ * @param characters_bytes (optional) number of bytes in |characters|
+ * @param is_bold (optional, set to NULL to ignore, in/out)
+ * @param is_italic (optional, set to NULL to ignore, in/out)
+ * @return true iff successful.
+ * Note that |filefaceid| uniquely identifies <font file, face_index) :
+ * system font: filefaceid =
+ * (fileid(unique per font file) << 4 | face_index)
+ * remote font: filefaceid = fileid
+ */
+ virtual bool Match(
+ std::string* result_family,
+ unsigned* result_filefaceid,
+ bool filefaceid_valid,
+ unsigned filefaceid,
+ const std::string& family,
+ const void* characters,
+ size_t characters_bytes,
+ bool* is_bold,
+ bool* is_italic) = 0;
+
+ /** Open a font file given the filefaceid as returned by Match.
+ */
+ virtual int Open(unsigned filefaceid) = 0;
+
+ static const unsigned kMaxFontFamilyLength = 2048;
+};
+
+#endif // FontConfigInterface_DEFINED
diff --git a/skia/ext/SkMemory_new_handler.cpp b/skia/ext/SkMemory_new_handler.cpp
new file mode 100644
index 0000000000..1799618efa
--- /dev/null
+++ b/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_util.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/skia/ext/SkThread_chrome.cc b/skia/ext/SkThread_chrome.cc
new file mode 100644
index 0000000000..4904f2da47
--- /dev/null
+++ b/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::Aquire_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/skia/ext/SkTypeface_fake.cpp b/skia/ext/SkTypeface_fake.cpp
new file mode 100644
index 0000000000..dea36a1ff4
--- /dev/null
+++ b/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/skia/ext/bitmap_platform_device.h b/skia/ext/bitmap_platform_device.h
new file mode 100644
index 0000000000..56e41763f8
--- /dev/null
+++ b/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/skia/ext/bitmap_platform_device_android.cc b/skia/ext/bitmap_platform_device_android.cc
new file mode 100644
index 0000000000..6a71fb22ba
--- /dev/null
+++ b/skia/ext/bitmap_platform_device_android.cc
@@ -0,0 +1,89 @@
+// 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);
+}
+
+// Port of PlatformBitmap to android
+
+PlatformBitmap::~PlatformBitmap() {}
+
+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/skia/ext/bitmap_platform_device_android.h b/skia/ext/bitmap_platform_device_android.h
new file mode 100644
index 0000000000..b5755cb4b0
--- /dev/null
+++ b/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/skia/ext/bitmap_platform_device_data.h b/skia/ext/bitmap_platform_device_data.h
new file mode 100644
index 0000000000..81e81ed79f
--- /dev/null
+++ b/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/skia/ext/bitmap_platform_device_linux.cc b/skia/ext/bitmap_platform_device_linux.cc
new file mode 100644
index 0000000000..63b44a6f07
--- /dev/null
+++ b/skia/ext/bitmap_platform_device_linux.cc
@@ -0,0 +1,203 @@
+// 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 (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);
+}
+
+// Port of PlatformBitmap to linux
+
+PlatformBitmap::~PlatformBitmap() {
+ cairo_destroy(surface_);
+}
+
+bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
+ cairo_surface_t* surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+ width, height);
+ if (cairo_surface_status(surf) != CAIRO_STATUS_SUCCESS) {
+ cairo_surface_destroy(surf);
+ return false;
+ }
+
+ bitmap_.setConfig(SkBitmap::kARGB_8888_Config, width, height,
+ cairo_image_surface_get_stride(surf));
+ bitmap_.setPixels(cairo_image_surface_get_data(surf));
+ bitmap_.setIsOpaque(is_opaque);
+
+ surface_ = cairo_create(surf);
+ cairo_surface_destroy(surf);
+ return true;
+}
+
+} // namespace skia
diff --git a/skia/ext/bitmap_platform_device_linux.h b/skia/ext/bitmap_platform_device_linux.h
new file mode 100644
index 0000000000..e1f9a75c85
--- /dev/null
+++ b/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/skia/ext/bitmap_platform_device_mac.cc b/skia/ext/bitmap_platform_device_mac.cc
new file mode 100644
index 0000000000..d8f16a2d28
--- /dev/null
+++ b/skia/ext/bitmap_platform_device_mac.cc
@@ -0,0 +1,282 @@
+// 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.
+ 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(
+ 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(
+ BitmapPlatformDeviceData* data, const SkBitmap& bitmap)
+ : SkDevice(bitmap),
+ data_(data) {
+ SetPlatformDevice(this, this);
+}
+
+BitmapPlatformDevice::~BitmapPlatformDevice() {
+ data_->unref();
+}
+
+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;
+}
+
+// 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/skia/ext/bitmap_platform_device_mac.h b/skia/ext/bitmap_platform_device_mac.h
new file mode 100644
index 0000000000..0d00a0abb5
--- /dev/null
+++ b/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"
+
+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(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. We hold a reference
+ // to this object.
+ BitmapPlatformDeviceData* data_;
+
+ DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDevice);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_BITMAP_PLATFORM_DEVICE_MAC_H_
diff --git a/skia/ext/bitmap_platform_device_mac_unittest.cc b/skia/ext/bitmap_platform_device_mac_unittest.cc
new file mode 100644
index 0000000000..7265bc4cfb
--- /dev/null
+++ b/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/skia/ext/bitmap_platform_device_win.cc b/skia/ext/bitmap_platform_device_win.cc
new file mode 100644
index 0000000000..dc21673f8c
--- /dev/null
+++ b/skia/ext/bitmap_platform_device_win.cc
@@ -0,0 +1,299 @@
+// 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"
+
+static HBITMAP CreateHBitmap(int width, int height, bool is_opaque,
+ HANDLE shared_section, SkBitmap* output) {
+ // 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;
+
+ void* data = NULL;
+ HBITMAP hbitmap = CreateDIBSection(NULL, reinterpret_cast<BITMAPINFO*>(&hdr),
+ 0, &data, shared_section, 0);
+ if (hbitmap) {
+ output->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+ output->setPixels(data);
+ output->setIsOpaque(is_opaque);
+ }
+ return hbitmap;
+}
+
+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) {
+ SkBitmap bitmap;
+
+ HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, shared_section,
+ &bitmap);
+ if (!hbitmap)
+ return NULL;
+
+#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(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(
+ 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);
+ data_->unref();
+}
+
+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 /*usage*/) {
+ SkASSERT(config == SkBitmap::kARGB_8888_Config);
+ SkDevice* bitmap_device = BitmapPlatformDevice::CreateAndClear(width, height,
+ isOpaque);
+ return bitmap_device;
+}
+
+// Port of PlatformBitmap to win
+
+PlatformBitmap::~PlatformBitmap() {
+ if (surface_)
+ DeleteDC(surface_);
+
+ HBITMAP hbitmap = (HBITMAP)platform_extra_;
+ if (hbitmap)
+ DeleteObject(hbitmap);
+}
+
+bool PlatformBitmap::Allocate(int width, int height, bool is_opaque) {
+ HBITMAP hbitmap = CreateHBitmap(width, height, is_opaque, 0, &bitmap_);
+ if (!hbitmap)
+ return false;
+
+ surface_ = CreateCompatibleDC(NULL);
+ InitializeDC(surface_);
+ HGDIOBJ old_bitmap = SelectObject(surface_, hbitmap);
+ // 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);
+ // remember the hbitmap, so we can free it in our destructor
+ platform_extra_ = (intptr_t)hbitmap;
+ return true;
+}
+
+} // namespace skia
diff --git a/skia/ext/bitmap_platform_device_win.h b/skia/ext/bitmap_platform_device_win.h
new file mode 100644
index 0000000000..745ac10d68
--- /dev/null
+++ b/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"
+
+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. The data should already be ref'ed for us.
+ BitmapPlatformDevice(BitmapPlatformDeviceData* data,
+ const SkBitmap& bitmap);
+
+ // Data associated with this device, guaranteed non-null. We hold a reference
+ // to this object.
+ 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/skia/ext/convolver.cc b/skia/ext/convolver.cc
new file mode 100644
index 0000000000..ee9d056fa4
--- /dev/null
+++ b/skia/ext/convolver.cc
@@ -0,0 +1,860 @@
+// 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 "third_party/skia/include/core/SkTypes.h"
+
+#if defined(SIMD_SSE2)
+#include <emmintrin.h> // ARCH_CPU_X86_FAMILY was defined in build/config.h
+#endif
+
+namespace skia {
+
+namespace {
+
+// Converts the argument to an 8-bit unsigned value by clamping to the range
+// 0-255.
+inline unsigned char ClampTo8(int a) {
+ if (static_cast<unsigned>(a) < 256)
+ return a; // Avoid the extra check in the common case.
+ if (a < 0)
+ return 0;
+ return 255;
+}
+
+// Stores a list of rows in a circular buffer. The usage is you write into it
+// by calling AdvanceRow. It will keep track of which row in the buffer it
+// should use next, and the total number of rows added.
+class CircularRowBuffer {
+ public:
+ // The number of pixels in each row is given in |source_row_pixel_width|.
+ // The maximum number of rows needed in the buffer is |max_y_filter_size|
+ // (we only need to store enough rows for the biggest filter).
+ //
+ // We use the |first_input_row| to compute the coordinates of all of the
+ // following rows returned by Advance().
+ CircularRowBuffer(int dest_row_pixel_width, int max_y_filter_size,
+ int first_input_row)
+ : row_byte_width_(dest_row_pixel_width * 4),
+ num_rows_(max_y_filter_size),
+ next_row_(0),
+ next_row_coordinate_(first_input_row) {
+ buffer_.resize(row_byte_width_ * max_y_filter_size);
+ row_addresses_.resize(num_rows_);
+ }
+
+ // Moves to the next row in the buffer, returning a pointer to the beginning
+ // of it.
+ unsigned char* AdvanceRow() {
+ unsigned char* row = &buffer_[next_row_ * row_byte_width_];
+ next_row_coordinate_++;
+
+ // Set the pointer to the next row to use, wrapping around if necessary.
+ next_row_++;
+ if (next_row_ == num_rows_)
+ next_row_ = 0;
+ return row;
+ }
+
+ // Returns a pointer to an "unrolled" array of rows. These rows will start
+ // at the y coordinate placed into |*first_row_index| and will continue in
+ // order for the maximum number of rows in this circular buffer.
+ //
+ // The |first_row_index_| may be negative. This means the circular buffer
+ // starts before the top of the image (it hasn't been filled yet).
+ unsigned char* const* GetRowAddresses(int* first_row_index) {
+ // Example for a 4-element circular buffer holding coords 6-9.
+ // Row 0 Coord 8
+ // Row 1 Coord 9
+ // Row 2 Coord 6 <- next_row_ = 2, next_row_coordinate_ = 10.
+ // Row 3 Coord 7
+ //
+ // The "next" row is also the first (lowest) coordinate. This computation
+ // may yield a negative value, but that's OK, the math will work out
+ // since the user of this buffer will compute the offset relative
+ // to the first_row_index and the negative rows will never be used.
+ *first_row_index = next_row_coordinate_ - num_rows_;
+
+ int cur_row = next_row_;
+ for (int i = 0; i < num_rows_; i++) {
+ row_addresses_[i] = &buffer_[cur_row * row_byte_width_];
+
+ // Advance to the next row, wrapping if necessary.
+ cur_row++;
+ if (cur_row == num_rows_)
+ cur_row = 0;
+ }
+ return &row_addresses_[0];
+ }
+
+ private:
+ // The buffer storing the rows. They are packed, each one row_byte_width_.
+ std::vector<unsigned char> buffer_;
+
+ // Number of bytes per row in the |buffer_|.
+ int row_byte_width_;
+
+ // The number of rows available in the buffer.
+ int num_rows_;
+
+ // The next row index we should write into. This wraps around as the
+ // circular buffer is used.
+ int next_row_;
+
+ // The y coordinate of the |next_row_|. This is incremented each time a
+ // new row is appended and does not wrap.
+ int next_row_coordinate_;
+
+ // Buffer used by GetRowAddresses().
+ std::vector<unsigned char*> row_addresses_;
+};
+
+// 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;
+ }
+ }
+}
+
+
+// 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) {
+#if defined(SIMD_SSE2)
+ 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;
+ }
+#endif
+}
+
+// Convolves horizontally along four rows. The row data is given in
+// |src_data| and continues for the num_values() of the filter.
+// The algorithm is almost same as |ConvolveHorizontally_SSE2|. Please
+// refer to that function for detailed comments.
+void ConvolveHorizontally4_SSE2(const unsigned char* src_data[4],
+ const ConvolutionFilter1D& filter,
+ unsigned char* out_row[4]) {
+#if defined(SIMD_SSE2)
+ 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;
+ }
+#endif
+}
+
+// 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) {
+#if defined(SIMD_SSE2)
+ 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;
+ }
+ }
+#endif
+}
+
+} // 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 first_non_zero = 0;
+ while (first_non_zero < filter_length && filter_values[first_non_zero] == 0)
+ first_non_zero++;
+
+ if (first_non_zero < filter_length) {
+ // Here we have at least one non-zero factor.
+ int last_non_zero = filter_length - 1;
+ while (last_non_zero >= 0 && filter_values[last_non_zero] == 0)
+ last_non_zero--;
+
+ filter_offset += first_non_zero;
+ filter_length = last_non_zero + 1 - first_non_zero;
+ SkASSERT(filter_length > 0);
+
+ for (int i = first_non_zero; i <= last_non_zero; i++)
+ filter_values_.push_back(filter_values[i]);
+ } else {
+ // Here all the factors were zeroes.
+ filter_length = 0;
+ }
+
+ FilterInstance instance;
+
+ // We pushed filter_length elements onto filter_values_
+ instance.data_location = (static_cast<int>(filter_values_.size()) -
+ filter_length);
+ instance.offset = filter_offset;
+ instance.length = filter_length;
+ filters_.push_back(instance);
+
+ max_filter_ = std::max(max_filter_, filter_length);
+}
+
+void BGRAConvolve2D(const unsigned char* source_data,
+ int source_byte_row_stride,
+ bool source_has_alpha,
+ const ConvolutionFilter1D& filter_x,
+ const ConvolutionFilter1D& filter_y,
+ int output_byte_row_stride,
+ unsigned char* output,
+ bool use_sse2) {
+#if !defined(SIMD_SSE2)
+ // Even we have runtime support for SSE2 instructions, since the binary
+ // was not built with SSE2 support, we had to fallback to C version.
+ use_sse2 = false;
+#endif
+
+ int max_y_filter_size = filter_y.max_filter();
+
+ // The next row in the input that we will generate a horizontally
+ // convolved row for. If the filter doesn't start at the beginning of the
+ // image (this is the case when we are only resizing a subset), then we
+ // don't want to generate any output rows before that. Compute the starting
+ // row for convolution as the first pixel for the first vertical filter.
+ int filter_offset, filter_length;
+ const ConvolutionFilter1D::Fixed* filter_values =
+ filter_y.FilterForValue(0, &filter_offset, &filter_length);
+ int next_x_row = filter_offset;
+
+ // We loop over each row in the input doing a horizontal convolution. This
+ // will result in a horizontally convolved image. We write the results into
+ // a circular buffer of convolved rows and do vertical convolution as rows
+ // are available. This prevents us from having to store the entire
+ // intermediate image and helps cache coherency.
+ // We will need four extra rows to allow horizontal convolution could be done
+ // simultaneously. We also padding each row in row buffer to be aligned-up to
+ // 16 bytes.
+ // TODO(jiesun): We do not use aligned load from row buffer in vertical
+ // convolution pass yet. Somehow Windows does not like it.
+ int row_buffer_width = (filter_x.num_values() + 15) & ~0xF;
+ int row_buffer_height = max_y_filter_size + (use_sse2 ? 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;
+ filter_y.FilterForValue(num_output_rows - 1, &last_filter_offset,
+ &last_filter_length);
+
+ for (int out_y = 0; out_y < num_output_rows; out_y++) {
+ filter_values = filter_y.FilterForValue(out_y,
+ &filter_offset, &filter_length);
+
+ // Generate output rows until we have enough to run the current filter.
+ if (use_sse2) {
+ while (next_x_row < filter_offset + filter_length) {
+ if (next_x_row + 3 < last_filter_offset + last_filter_length - 1) {
+ const unsigned char* src[4];
+ unsigned char* out_row[4];
+ for (int i = 0; i < 4; ++i) {
+ src[i] = &source_data[(next_x_row + i) * source_byte_row_stride];
+ out_row[i] = row_buffer.AdvanceRow();
+ }
+ ConvolveHorizontally4_SSE2(src, filter_x, out_row);
+ next_x_row += 4;
+ } else {
+ // For the last row, SSE2 load possibly to access data beyond the
+ // image area. therefore we use C version here.
+ if (next_x_row == last_filter_offset + last_filter_length - 1) {
+ 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());
+ }
+ } else {
+ ConvolveHorizontally_SSE2(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ }
+ next_x_row++;
+ }
+ }
+ } else {
+ while (next_x_row < filter_offset + filter_length) {
+ if (source_has_alpha) {
+ ConvolveHorizontally<true>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ } else {
+ ConvolveHorizontally<false>(
+ &source_data[next_x_row * source_byte_row_stride],
+ filter_x, row_buffer.AdvanceRow());
+ }
+ next_x_row++;
+ }
+ }
+
+ // Compute where in the output image this row of final data will go.
+ unsigned char* cur_output_row = &output[out_y * output_byte_row_stride];
+
+ // Get the list of rows that the circular buffer has, in order.
+ int first_row_in_circular_buffer;
+ unsigned char* const* rows_to_convolve =
+ row_buffer.GetRowAddresses(&first_row_in_circular_buffer);
+
+ // Now compute the start of the subset of those rows that the filter
+ // needs.
+ unsigned char* const* first_row_for_filter =
+ &rows_to_convolve[filter_offset - first_row_in_circular_buffer];
+
+ if (source_has_alpha) {
+ if (use_sse2) {
+ ConvolveVertically_SSE2<true>(filter_values, filter_length,
+ first_row_for_filter,
+ filter_x.num_values(), cur_output_row);
+ } else {
+ ConvolveVertically<true>(filter_values, filter_length,
+ first_row_for_filter,
+ filter_x.num_values(), cur_output_row);
+ }
+ } else {
+ if (use_sse2) {
+ ConvolveVertically_SSE2<false>(filter_values, filter_length,
+ first_row_for_filter,
+ filter_x.num_values(), cur_output_row);
+ } else {
+ ConvolveVertically<false>(filter_values, filter_length,
+ first_row_for_filter,
+ filter_x.num_values(), cur_output_row);
+ }
+ }
+ }
+}
+
+} // namespace skia
diff --git a/skia/ext/convolver.h b/skia/ext/convolver.h
new file mode 100644
index 0000000000..14974e5293
--- /dev/null
+++ b/skia/ext/convolver.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_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/SkTypes.h"
+
+#if defined(ARCH_CPU_X86_FAMILY)
+#if defined(__x86_64__) || defined(_M_X64) || defined(__SSE2__) || _M_IX86_FP==2
+// This is where we had compiler support for SSE2 instructions.
+// FIXME: Known buggy, so disabling for M22.
+// #define SIMD_SSE2 1
+#endif
+#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.length;
+ if (filter.length == 0) {
+ return NULL;
+ }
+ return &filter_values_[filter.data_location];
+ }
+
+
+ inline void PaddingForSIMD(int padding_count) {
+ // Padding |padding_count| of more dummy coefficients after the coefficients
+ // of last filter to prevent SIMD instructions which load 8 or 16 bytes
+ // together to access invalid memory areas. We are not trying to align the
+ // coefficients right now due to the opaqueness of <vector> implementation.
+ // This has to be done after all |AddFilter| calls.
+ for (int i = 0; i < padding_count; ++i)
+ filter_values_.push_back(static_cast<Fixed>(0));
+ }
+
+ private:
+ struct FilterInstance {
+ // Offset within filter_values for this instance of the filter.
+ int data_location;
+
+ // Distance from the left of the filter to the center. IN PIXELS
+ int offset;
+
+ // Number of values in this filter instance.
+ int length;
+ };
+
+ // Stores the information for each filter added to this class.
+ std::vector<FilterInstance> filters_;
+
+ // We store all the filter values in this flat list, indexed by
+ // |FilterInstance.data_location| to avoid the mallocs required for storing
+ // each one separately.
+ std::vector<Fixed> filter_values_;
+
+ // The maximum size of any filter we've added.
+ int max_filter_;
+};
+
+// Does a two-dimensional convolution on the given source image.
+//
+// It is assumed the source pixel offsets referenced in the input filters
+// reference only valid pixels, so the source image size is not required. Each
+// row of the source image starts |source_byte_row_stride| after the previous
+// one (this allows you to have rows with some padding at the end).
+//
+// The result will be put into the given output buffer. The destination image
+// size will be xfilter.num_values() * yfilter.num_values() pixels. It will be
+// in rows of exactly xfilter.num_values() * 4 bytes.
+//
+// |source_has_alpha| is a hint that allows us to avoid doing computations on
+// the alpha channel if the image is opaque. If you don't know, set this to
+// true and it will work properly, but setting this to false will be a few
+// percent faster if you know the image is opaque.
+//
+// The layout in memory is assumed to be 4-bytes per pixel in B-G-R-A order
+// (this is ARGB when loaded into 32-bit words on a little-endian machine).
+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_sse2);
+} // namespace skia
+
+#endif // SKIA_EXT_CONVOLVER_H_
diff --git a/skia/ext/convolver_unittest.cc b/skia/ext/convolver_unittest.cc
new file mode 100644
index 0000000000..f61b685daf
--- /dev/null
+++ b/skia/ext/convolver_unittest.cc
@@ -0,0 +1,321 @@
+// 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 <vector>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/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);
+}
+
+TEST(Convolver, SIMDVerification) {
+#if defined(SIMD_SSE2)
+ base::CPU cpu;
+ if (!cpu.has_sse2()) return;
+
+ int source_sizes[][2] = { {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 = source_sizes[j][0];
+ unsigned int dest_height = source_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;
+ if (offset > source_width - arraysize(filter))
+ offset = source_width - arraysize(filter);
+ x_filter.AddFilter(offset, filter, arraysize(filter));
+ }
+ for (unsigned int p = 0; p < dest_height; ++p) {
+ unsigned int offset = source_height * p / dest_height;
+ if (offset > source_height - arraysize(filter))
+ offset = source_height - arraysize(filter);
+ y_filter.AddFilter(offset, filter, arraysize(filter));
+ }
+
+ // 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 (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();
+ }
+ }
+ }
+ }
+#endif
+}
+
+} // namespace skia
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/00_pc_clean.png b/skia/ext/data/vectorcanvastest/basicdrawing/00_pc_clean.png
new file mode 100644
index 0000000000..a5435f278b
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/00_pc_clean.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/00_vc_clean.png b/skia/ext/data/vectorcanvastest/basicdrawing/00_vc_clean.png
new file mode 100644
index 0000000000..a5435f278b
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/00_vc_clean.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png b/skia/ext/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png
new file mode 100644
index 0000000000..a5435f278b
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/01_pc_drawargb.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png b/skia/ext/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png
new file mode 100644
index 0000000000..a5435f278b
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/01_vc_drawargb.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png b/skia/ext/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png
new file mode 100644
index 0000000000..c21fdf1c57
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/02_pc_drawline_black.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png b/skia/ext/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png
new file mode 100644
index 0000000000..c21fdf1c57
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/02_vc_drawline_black.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png b/skia/ext/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png
new file mode 100644
index 0000000000..dfc46a80d8
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/03_pc_drawrect_green.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png b/skia/ext/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png
new file mode 100644
index 0000000000..dfc46a80d8
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/03_vc_drawrect_green.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png b/skia/ext/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png
new file mode 100644
index 0000000000..dfc46a80d8
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/04_pc_drawrect_noop.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png b/skia/ext/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png
new file mode 100644
index 0000000000..dfc46a80d8
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/04_vc_drawrect_noop.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png b/skia/ext/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png
new file mode 100644
index 0000000000..69cc6dce63
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/05_pc_drawrect_noop.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png b/skia/ext/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png
new file mode 100644
index 0000000000..69cc6dce63
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/05_vc_drawrect_noop.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png b/skia/ext/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png
new file mode 100644
index 0000000000..9cbff6e164
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/06_pc_drawpaint_black.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png b/skia/ext/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png
new file mode 100644
index 0000000000..9cbff6e164
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/06_vc_drawpaint_black.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png b/skia/ext/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png
new file mode 100644
index 0000000000..bbdfc36cb5
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/07_pc_drawline_left_to_right.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png b/skia/ext/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png
new file mode 100644
index 0000000000..bbdfc36cb5
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/07_vc_drawline_left_to_right.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png b/skia/ext/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png
new file mode 100644
index 0000000000..9dc35f051e
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/08_pc_drawline_red.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png b/skia/ext/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png
new file mode 100644
index 0000000000..9dc35f051e
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/basicdrawing/08_vc_drawline_red.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/bitmaps/00_pc_opaque.png b/skia/ext/data/vectorcanvastest/bitmaps/00_pc_opaque.png
new file mode 100644
index 0000000000..812b1ca293
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/bitmaps/00_pc_opaque.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/bitmaps/00_vc_opaque.png b/skia/ext/data/vectorcanvastest/bitmaps/00_vc_opaque.png
new file mode 100644
index 0000000000..812b1ca293
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/bitmaps/00_vc_opaque.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/bitmaps/01_pc_alpha.png b/skia/ext/data/vectorcanvastest/bitmaps/01_pc_alpha.png
new file mode 100644
index 0000000000..1d1342bbaa
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/bitmaps/01_pc_alpha.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/bitmaps/01_vc_alpha.png b/skia/ext/data/vectorcanvastest/bitmaps/01_vc_alpha.png
new file mode 100644
index 0000000000..1d1342bbaa
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/bitmaps/01_vc_alpha.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/bitmaps/bitmap_alpha.png b/skia/ext/data/vectorcanvastest/bitmaps/bitmap_alpha.png
new file mode 100644
index 0000000000..a19d09d06c
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/bitmaps/bitmap_alpha.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/bitmaps/bitmap_opaque.png b/skia/ext/data/vectorcanvastest/bitmaps/bitmap_opaque.png
new file mode 100644
index 0000000000..3560d270f5
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/bitmaps/bitmap_opaque.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/00_pc_circle_stroke.png b/skia/ext/data/vectorcanvastest/circles/00_pc_circle_stroke.png
new file mode 100644
index 0000000000..896631b323
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/00_pc_circle_stroke.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/00_vc_circle_stroke.png b/skia/ext/data/vectorcanvastest/circles/00_vc_circle_stroke.png
new file mode 100644
index 0000000000..c265be341f
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/00_vc_circle_stroke.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/01_pc_circle_fill.png b/skia/ext/data/vectorcanvastest/circles/01_pc_circle_fill.png
new file mode 100644
index 0000000000..92b647d046
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/01_pc_circle_fill.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/01_vc_circle_fill.png b/skia/ext/data/vectorcanvastest/circles/01_vc_circle_fill.png
new file mode 100644
index 0000000000..f5270cea5a
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/01_vc_circle_fill.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/02_pc_circle_over_strike.png b/skia/ext/data/vectorcanvastest/circles/02_pc_circle_over_strike.png
new file mode 100644
index 0000000000..64ae06ab9d
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/02_pc_circle_over_strike.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/02_vc_circle_over_strike.png b/skia/ext/data/vectorcanvastest/circles/02_vc_circle_over_strike.png
new file mode 100644
index 0000000000..4d3d1b069e
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/02_vc_circle_over_strike.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png b/skia/ext/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png
new file mode 100644
index 0000000000..6aeeb49b08
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/03_pc_circle_stroke_and_fill.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png b/skia/ext/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png
new file mode 100644
index 0000000000..f073a3e8c8
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/03_vc_circle_stroke_and_fill.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/04_pc_mixed_stroke.png b/skia/ext/data/vectorcanvastest/circles/04_pc_mixed_stroke.png
new file mode 100644
index 0000000000..e4a044fa26
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/04_pc_mixed_stroke.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/circles/04_vc_mixed_stroke.png b/skia/ext/data/vectorcanvastest/circles/04_vc_mixed_stroke.png
new file mode 100644
index 0000000000..efd9e3abdf
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/circles/04_vc_mixed_stroke.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingclean/00_pc_clipped.png b/skia/ext/data/vectorcanvastest/clippingclean/00_pc_clipped.png
new file mode 100644
index 0000000000..14ff949d62
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingclean/00_pc_clipped.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingclean/00_vc_clipped.png b/skia/ext/data/vectorcanvastest/clippingclean/00_vc_clipped.png
new file mode 100644
index 0000000000..14ff949d62
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingclean/00_vc_clipped.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingclean/01_pc_unclipped.png b/skia/ext/data/vectorcanvastest/clippingclean/01_pc_unclipped.png
new file mode 100644
index 0000000000..436f9a5b7c
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingclean/01_pc_unclipped.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingclean/01_vc_unclipped.png b/skia/ext/data/vectorcanvastest/clippingclean/01_vc_unclipped.png
new file mode 100644
index 0000000000..436f9a5b7c
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingclean/01_vc_unclipped.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingcombined/00_pc_combined.png b/skia/ext/data/vectorcanvastest/clippingcombined/00_pc_combined.png
new file mode 100644
index 0000000000..14ff949d62
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingcombined/00_pc_combined.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingcombined/00_vc_combined.png b/skia/ext/data/vectorcanvastest/clippingcombined/00_vc_combined.png
new file mode 100644
index 0000000000..14ff949d62
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingcombined/00_vc_combined.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingintersect/00_pc_intersect.png b/skia/ext/data/vectorcanvastest/clippingintersect/00_pc_intersect.png
new file mode 100644
index 0000000000..1285dac0db
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingintersect/00_pc_intersect.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingintersect/00_vc_intersect.png b/skia/ext/data/vectorcanvastest/clippingintersect/00_vc_intersect.png
new file mode 100644
index 0000000000..1285dac0db
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingintersect/00_vc_intersect.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingpath/00_pc_path.png b/skia/ext/data/vectorcanvastest/clippingpath/00_pc_path.png
new file mode 100644
index 0000000000..8807649bb9
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingpath/00_pc_path.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingpath/00_vc_path.png b/skia/ext/data/vectorcanvastest/clippingpath/00_vc_path.png
new file mode 100644
index 0000000000..8807649bb9
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingpath/00_vc_path.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingrect/00_pc_rect.png b/skia/ext/data/vectorcanvastest/clippingrect/00_pc_rect.png
new file mode 100644
index 0000000000..9c365e13dd
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingrect/00_pc_rect.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/clippingrect/00_vc_rect.png b/skia/ext/data/vectorcanvastest/clippingrect/00_vc_rect.png
new file mode 100644
index 0000000000..9c365e13dd
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/clippingrect/00_vc_rect.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/diagonallines/00_pc_nw-se.png b/skia/ext/data/vectorcanvastest/diagonallines/00_pc_nw-se.png
new file mode 100644
index 0000000000..5736c3503a
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/diagonallines/00_pc_nw-se.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/diagonallines/00_vc_nw-se.png b/skia/ext/data/vectorcanvastest/diagonallines/00_vc_nw-se.png
new file mode 100644
index 0000000000..5736c3503a
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/diagonallines/00_vc_nw-se.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png b/skia/ext/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png
new file mode 100644
index 0000000000..bfffd8a52a
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/diagonallines/01_pc_sw-ne.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png b/skia/ext/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png
new file mode 100644
index 0000000000..ae6b7537fc
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/diagonallines/01_vc_sw-ne.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png b/skia/ext/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png
new file mode 100644
index 0000000000..75acdad042
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/diagonallines/02_pc_ne-sw.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png b/skia/ext/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png
new file mode 100644
index 0000000000..86a67992af
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/diagonallines/02_vc_ne-sw.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/diagonallines/03_pc_se-nw.png b/skia/ext/data/vectorcanvastest/diagonallines/03_pc_se-nw.png
new file mode 100644
index 0000000000..50502cc1b8
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/diagonallines/03_pc_se-nw.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/diagonallines/03_vc_se-nw.png b/skia/ext/data/vectorcanvastest/diagonallines/03_vc_se-nw.png
new file mode 100644
index 0000000000..362f6e727e
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/diagonallines/03_vc_se-nw.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/lineorientation/00_pc_horizontal.png b/skia/ext/data/vectorcanvastest/lineorientation/00_pc_horizontal.png
new file mode 100644
index 0000000000..7bcd99885a
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/lineorientation/00_pc_horizontal.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/lineorientation/00_vc_horizontal.png b/skia/ext/data/vectorcanvastest/lineorientation/00_vc_horizontal.png
new file mode 100644
index 0000000000..46c9b0abe1
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/lineorientation/00_vc_horizontal.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/lineorientation/01_pc_vertical.png b/skia/ext/data/vectorcanvastest/lineorientation/01_pc_vertical.png
new file mode 100644
index 0000000000..09f41db50f
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/lineorientation/01_pc_vertical.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/lineorientation/01_vc_vertical.png b/skia/ext/data/vectorcanvastest/lineorientation/01_vc_vertical.png
new file mode 100644
index 0000000000..7f5f1f7f15
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/lineorientation/01_vc_vertical.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png b/skia/ext/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png
new file mode 100644
index 0000000000..5966df640e
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/lineorientation/02_pc_horizontal_180.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png b/skia/ext/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png
new file mode 100644
index 0000000000..e43a844985
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/lineorientation/02_vc_horizontal_180.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png b/skia/ext/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png
new file mode 100644
index 0000000000..9ac482558f
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/lineorientation/03_pc_vertical_180.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png b/skia/ext/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png
new file mode 100644
index 0000000000..d9e033abf5
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/lineorientation/03_vc_vertical_180.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/matrix/00_pc_translate1.png b/skia/ext/data/vectorcanvastest/matrix/00_pc_translate1.png
new file mode 100644
index 0000000000..fe27cb3158
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/matrix/00_pc_translate1.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/matrix/00_vc_translate1.png b/skia/ext/data/vectorcanvastest/matrix/00_vc_translate1.png
new file mode 100644
index 0000000000..fe27cb3158
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/matrix/00_vc_translate1.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/matrix/01_pc_translate2.png b/skia/ext/data/vectorcanvastest/matrix/01_pc_translate2.png
new file mode 100644
index 0000000000..406bf5776e
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/matrix/01_pc_translate2.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/matrix/01_vc_translate2.png b/skia/ext/data/vectorcanvastest/matrix/01_vc_translate2.png
new file mode 100644
index 0000000000..406bf5776e
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/matrix/01_vc_translate2.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/matrix/02_pc_scale.png b/skia/ext/data/vectorcanvastest/matrix/02_pc_scale.png
new file mode 100644
index 0000000000..9e94fb0c6f
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/matrix/02_pc_scale.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/matrix/02_vc_scale.png b/skia/ext/data/vectorcanvastest/matrix/02_vc_scale.png
new file mode 100644
index 0000000000..fde62aadcb
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/matrix/02_vc_scale.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/matrix/03_pc_rotate.png b/skia/ext/data/vectorcanvastest/matrix/03_pc_rotate.png
new file mode 100644
index 0000000000..7a43a2ad66
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/matrix/03_pc_rotate.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/matrix/03_vc_rotate.png b/skia/ext/data/vectorcanvastest/matrix/03_vc_rotate.png
new file mode 100644
index 0000000000..7a22b7f14d
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/matrix/03_vc_rotate.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/patheffects/00_pc_dash_line.png b/skia/ext/data/vectorcanvastest/patheffects/00_pc_dash_line.png
new file mode 100644
index 0000000000..e08d3e2a3f
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/patheffects/00_pc_dash_line.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/patheffects/00_vc_dash_line.png b/skia/ext/data/vectorcanvastest/patheffects/00_vc_dash_line.png
new file mode 100644
index 0000000000..e08d3e2a3f
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/patheffects/00_vc_dash_line.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/patheffects/01_pc_dash_path.png b/skia/ext/data/vectorcanvastest/patheffects/01_pc_dash_path.png
new file mode 100644
index 0000000000..3a30135421
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/patheffects/01_pc_dash_path.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/patheffects/01_vc_dash_path.png b/skia/ext/data/vectorcanvastest/patheffects/01_vc_dash_path.png
new file mode 100644
index 0000000000..7868b9a4ed
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/patheffects/01_vc_dash_path.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/patheffects/02_pc_dash_rect.png b/skia/ext/data/vectorcanvastest/patheffects/02_pc_dash_rect.png
new file mode 100644
index 0000000000..04f2cebae2
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/patheffects/02_pc_dash_rect.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/patheffects/02_vc_dash_rect.png b/skia/ext/data/vectorcanvastest/patheffects/02_vc_dash_rect.png
new file mode 100644
index 0000000000..5344eee2e8
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/patheffects/02_vc_dash_rect.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/patheffects/03_pc_circle.png b/skia/ext/data/vectorcanvastest/patheffects/03_pc_circle.png
new file mode 100644
index 0000000000..4c267ef765
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/patheffects/03_pc_circle.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/patheffects/03_vc_circle.png b/skia/ext/data/vectorcanvastest/patheffects/03_vc_circle.png
new file mode 100644
index 0000000000..46ac35d05b
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/patheffects/03_vc_circle.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png b/skia/ext/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png
new file mode 100644
index 0000000000..cefbf8700b
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/pathorientation/00_pc_drawpath_ltr.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png b/skia/ext/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png
new file mode 100644
index 0000000000..cefbf8700b
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/pathorientation/00_vc_drawpath_ltr.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png b/skia/ext/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png
new file mode 100644
index 0000000000..7bcd99885a
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/pathorientation/01_pc_drawpath_rtl.png
Binary files differ
diff --git a/skia/ext/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png b/skia/ext/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png
new file mode 100644
index 0000000000..46c9b0abe1
--- /dev/null
+++ b/skia/ext/data/vectorcanvastest/pathorientation/01_vc_drawpath_rtl.png
Binary files differ
diff --git a/skia/ext/google_logging.cc b/skia/ext/google_logging.cc
new file mode 100644
index 0000000000..5e8ffe1d14
--- /dev/null
+++ b/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/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/skia/ext/image_operations.cc b/skia/ext/image_operations.cc
new file mode 100644
index 0000000000..b048f3064a
--- /dev/null
+++ b/skia/ext/image_operations.cc
@@ -0,0 +1,541 @@
+// 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.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/SkBitmap.h"
+#include "third_party/skia/include/core/SkRect.h"
+#include "third_party/skia/include/core/SkFontHost.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, float src_support,
+ 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);
+
+ x_filter_support_ = GetFilterSupport(scale_x);
+ y_filter_support_ = GetFilterSupport(scale_y);
+
+ // Support of the filter in source space.
+ float src_x_support = x_filter_support_ / scale_x;
+ float src_y_support = y_filter_support_ / scale_y;
+
+ ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(),
+ scale_x, src_x_support, &x_filter_);
+ ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(),
+ scale_y, src_y_support, &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, float src_support,
+ 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);
+
+ // 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(8);
+}
+
+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) {
+ if (method == ImageOperations::RESIZE_SUBPIXEL)
+ return ResizeSubpixel(source, dest_width, dest_height, dest_subset);
+ else
+ return ResizeBasic(source, method, dest_width, dest_height, dest_subset);
+}
+
+// static
+SkBitmap ImageOperations::ResizeSubpixel(const SkBitmap& source,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset) {
+ 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);
+ 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();
+ 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) {
+ 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())
+ 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.
+ base::CPU cpu;
+ SkBitmap result;
+ result.setConfig(SkBitmap::kARGB_8888_Config,
+ dest_subset.width(), dest_subset.height());
+ result.allocPixels();
+ 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()),
+ cpu.has_sse2());
+
+ // 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) {
+ SkIRect dest_subset = { 0, 0, dest_width, dest_height };
+ return Resize(source, method, dest_width, dest_height, dest_subset);
+}
+
+} // namespace skia
diff --git a/skia/ext/image_operations.h b/skia/ext/image_operations.h
new file mode 100644
index 0000000000..20e6a08dcc
--- /dev/null
+++ b/skia/ext/image_operations.h
@@ -0,0 +1,129 @@
+// 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/SkTypes.h"
+
+class SkBitmap;
+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);
+
+ // 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);
+
+ 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);
+
+ // Subpixel renderer.
+ static SkBitmap ResizeSubpixel(const SkBitmap& source,
+ int dest_width, int dest_height,
+ const SkIRect& dest_subset);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_IMAGE_OPERATIONS_H_
diff --git a/skia/ext/image_operations_bench.cc b/skia/ext/image_operations_bench.cc
new file mode 100644
index 0000000000..f590458ea7
--- /dev/null
+++ b/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/string_number_conversions.h"
+#include "base/string_split.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "base/utf_string_conversions.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/skia/ext/image_operations_unittest.cc b/skia/ext/image_operations_unittest.cc
new file mode 100644
index 0000000000..23353faacc
--- /dev/null
+++ b/skia/ext/image_operations_unittest.cc
@@ -0,0 +1,634 @@
+// 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 <iomanip>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_util.h"
+#include "base/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 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));
+ }
+ }
+}
+
+// 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
+ }
+}
diff --git a/skia/ext/platform_canvas.cc b/skia/ext/platform_canvas.cc
new file mode 100644
index 0000000000..11b1f5e9d5
--- /dev/null
+++ b/skia/ext/platform_canvas.cc
@@ -0,0 +1,92 @@
+// 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 {
+
+PlatformCanvas::PlatformCanvas() {}
+
+// static
+size_t PlatformCanvas::StrideForWidth(unsigned width) {
+ return 4 * width;
+}
+
+bool PlatformCanvas::initializeWithDevice(SkDevice* device) {
+ if (!device)
+ return false;
+
+ setDevice(device);
+ device->unref(); // Was created with refcount 1, and setDevice also refs.
+ return true;
+}
+
+SkCanvas* CreateBitmapCanvas(int width, int height, bool is_opaque) {
+ return new PlatformCanvas(width, height, is_opaque);
+}
+
+SkCanvas* TryCreateBitmapCanvas(int width, int height, bool is_opaque) {
+ PlatformCanvas* canvas = new PlatformCanvas();
+ if (!canvas->initialize(width, height, is_opaque)) {
+ delete canvas;
+ canvas = NULL;
+ }
+ return canvas;
+}
+
+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
+ paint.setXfermode(new SkProcXfermode(MakeOpaqueXfermodeProc))->unref();
+ canvas->drawRect(rect, paint);
+}
+
+PlatformBitmap::PlatformBitmap() : surface_(0), platform_extra_(0) {}
+
+} // namespace skia
diff --git a/skia/ext/platform_canvas.h b/skia/ext/platform_canvas.h
new file mode 100644
index 0000000000..f20d62e27e
--- /dev/null
+++ b/skia/ext/platform_canvas.h
@@ -0,0 +1,180 @@
+// 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 "third_party/skia/include/core/SkCanvas.h"
+
+namespace skia {
+
+// This class is a specialization of the regular SkCanvas that is designed to
+// work with a PlatformDevice to manage platform-specific drawing. It allows
+// using both Skia operations and platform-specific operations.
+class SK_API PlatformCanvas : public SkCanvas {
+ public:
+ // If you use the version with no arguments, you MUST call initialize()
+ PlatformCanvas();
+ // Set is_opaque if you are going to erase the bitmap and not use
+ // transparency: this will enable some optimizations.
+ PlatformCanvas(int width, int height, bool is_opaque);
+
+#if defined(WIN32)
+ // The shared_section parameter is passed to gfx::PlatformDevice::create.
+ // See it for details.
+ PlatformCanvas(int width, int height, bool is_opaque, HANDLE shared_section);
+#elif defined(__APPLE__)
+ PlatformCanvas(int width, int height, bool is_opaque,
+ CGContextRef context);
+ PlatformCanvas(int width, int height, bool is_opaque, uint8_t* context);
+#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.
+ PlatformCanvas(int width, int height, bool is_opaque, uint8_t* data);
+#endif
+
+ virtual ~PlatformCanvas();
+
+#if defined(WIN32)
+ // For two-part init, call if you use the no-argument constructor above. Note
+ // that we want this to optionally match the Linux initialize if you only
+ // pass 3 arguments, hence the evil default argument.
+ bool initialize(int width, int height, bool is_opaque,
+ HANDLE shared_section = NULL);
+#elif defined(__APPLE__)
+ // For two-part init, call if you use the no-argument constructor above
+ bool initialize(CGContextRef context, int width, int height, bool is_opaque);
+ bool initialize(int width, int height, bool is_opaque, uint8_t* data = NULL);
+
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
+ defined(__sun) || defined(ANDROID)
+ // For two-part init, call if you use the no-argument constructor above
+ bool initialize(int width, int height, bool is_opaque, uint8_t* data = NULL);
+#endif
+
+ // Shared --------------------------------------------------------------------
+
+ // 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.
+ static size_t StrideForWidth(unsigned width);
+
+ // Allow callers to see the non-virtual function even though we have an
+ // override of a virtual one.
+ // FIXME(brettw) is this necessary?
+ using SkCanvas::clipRect;
+
+ private:
+ // Helper method used internally by the initialize() methods.
+ bool initializeWithDevice(SkDevice* device);
+
+ // Disallow copy and assign
+ PlatformCanvas(const PlatformCanvas&);
+ PlatformCanvas& operator=(const PlatformCanvas&);
+};
+
+// 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.
+//
+// If there is no layer that is not all clipped out, this will return a
+// dummy device so callers do not have to check. If you are concerned about
+// performance, check the clip before doing any painting.
+//
+// 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);
+
+// Creates a canvas with raster bitmap backing.
+// Set is_opaque if you are going to erase the bitmap and not use
+// transparency: this will enable some optimizations.
+SK_API SkCanvas* CreateBitmapCanvas(int width, int height, bool is_opaque);
+
+// Non-crashing version of CreateBitmapCanvas
+// returns NULL if allocation fails for any reason.
+// Use this instead of CreateBitmapCanvas in places that are likely to
+// attempt to allocate very large canvases (therefore likely to fail),
+// and where it is possible to recover gracefully from the failed allocation.
+SK_API SkCanvas* TryCreateBitmapCanvas(int width, int height, bool is_opaque);
+
+// 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&);
+};
+
+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.
+ const SkBitmap& GetBitmap() { return bitmap_; }
+
+ private:
+ SkBitmap bitmap_;
+ PlatformSurface surface_; // initialized to 0
+ intptr_t platform_extra_; // initialized to 0, specific to each platform
+
+ DISALLOW_COPY_AND_ASSIGN(PlatformBitmap);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_PLATFORM_CANVAS_H_
diff --git a/skia/ext/platform_canvas_linux.cc b/skia/ext/platform_canvas_linux.cc
new file mode 100644
index 0000000000..9912c16e08
--- /dev/null
+++ b/skia/ext/platform_canvas_linux.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/platform_canvas.h"
+
+#include "base/debug/trace_event.h"
+#include "skia/ext/bitmap_platform_device.h"
+#include "skia/ext/platform_device.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+namespace skia {
+
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ if (!initialize(width, height, is_opaque))
+ SK_CRASH();
+}
+
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque,
+ uint8_t* data) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ if (!initialize(width, height, is_opaque, data))
+ SK_CRASH();
+}
+
+PlatformCanvas::~PlatformCanvas() {
+}
+
+bool PlatformCanvas::initialize(int width, int height, bool is_opaque,
+ uint8_t* data) {
+ return initializeWithDevice(BitmapPlatformDevice::Create(
+ width, height, is_opaque, data));
+}
+
+} // namespace skia
diff --git a/skia/ext/platform_canvas_mac.cc b/skia/ext/platform_canvas_mac.cc
new file mode 100644
index 0000000000..d1667c2e05
--- /dev/null
+++ b/skia/ext/platform_canvas_mac.cc
@@ -0,0 +1,56 @@
+// 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 "base/debug/trace_event.h"
+#include "skia/ext/bitmap_platform_device.h"
+#include "third_party/skia/include/core/SkTypes.h"
+
+namespace skia {
+
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ initialize(width, height, is_opaque);
+}
+
+PlatformCanvas::PlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ CGContextRef context) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ initialize(context, width, height, is_opaque);
+}
+
+PlatformCanvas::PlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ uint8_t* data) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ initialize(width, height, is_opaque, data);
+}
+
+PlatformCanvas::~PlatformCanvas() {
+}
+
+bool PlatformCanvas::initialize(int width,
+ int height,
+ bool is_opaque,
+ uint8_t* data) {
+ return initializeWithDevice(BitmapPlatformDevice::CreateWithData(
+ data, width, height, is_opaque));
+}
+
+bool PlatformCanvas::initialize(CGContextRef context,
+ int width,
+ int height,
+ bool is_opaque) {
+ return initializeWithDevice(BitmapPlatformDevice::Create(
+ context, width, height, is_opaque));
+}
+
+} // namespace skia
diff --git a/skia/ext/platform_canvas_skia.cc b/skia/ext/platform_canvas_skia.cc
new file mode 100644
index 0000000000..9cd51aca41
--- /dev/null
+++ b/skia/ext/platform_canvas_skia.cc
@@ -0,0 +1,113 @@
+// 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_canvas.h"
+
+#include "base/debug/trace_event.h"
+#include "skia/ext/bitmap_platform_device.h"
+
+// TODO(reveman): a lot of unnecessary duplication of code from
+// platform_canvas_[win|linux|mac].cc in here. Need to refactor
+// PlatformCanvas to avoid this:
+// http://code.google.com/p/chromium/issues/detail?id=119555
+
+namespace skia {
+
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ if (!initialize(width, height, is_opaque))
+ SK_CRASH();
+}
+
+#if defined(WIN32)
+PlatformCanvas::PlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ if (!initialize(width, height, is_opaque, shared_section))
+ SK_CRASH();
+}
+#elif defined(__APPLE__)
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque,
+ uint8_t* data) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ if (!initialize(width, height, is_opaque, data))
+ SK_CRASH();
+}
+PlatformCanvas::PlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ CGContextRef context) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ if (!initialize(context, width, height, is_opaque))
+ SK_CRASH();
+}
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
+ defined(__sun) || defined(ANDROID)
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque,
+ uint8_t* data) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ if (!initialize(width, height, is_opaque, data))
+ SK_CRASH();
+}
+#endif
+
+PlatformCanvas::~PlatformCanvas() {
+}
+
+#if defined(WIN32)
+bool PlatformCanvas::initialize(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section) {
+ // Use platform specific device for shared_section.
+ if (shared_section)
+ return initializeWithDevice(BitmapPlatformDevice::Create(
+ width, height, is_opaque, shared_section));
+
+ return initializeWithDevice(new SkDevice(
+ SkBitmap::kARGB_8888_Config, width, height, is_opaque));
+}
+#elif defined(__APPLE__)
+bool PlatformCanvas::initialize(int width,
+ int height,
+ bool is_opaque,
+ uint8_t* data) {
+ // Use platform specific device for data.
+ if (data)
+ return initializeWithDevice(BitmapPlatformDevice::CreateWithData(
+ data, width, height, is_opaque));
+
+ return initializeWithDevice(new SkDevice(
+ SkBitmap::kARGB_8888_Config, width, height, is_opaque));
+}
+
+bool PlatformCanvas::initialize(CGContextRef context,
+ int width,
+ int height,
+ bool is_opaque) {
+ return initializeWithDevice(BitmapPlatformDevice::Create(
+ context, width, height, is_opaque));
+}
+#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
+ defined(__sun) || defined(ANDROID)
+bool PlatformCanvas::initialize(int width, int height, bool is_opaque,
+ uint8_t* data) {
+ // Use platform specific device for data.
+ if (data)
+ return initializeWithDevice(BitmapPlatformDevice::Create(
+ width, height, is_opaque, data));
+
+ return initializeWithDevice(new SkDevice(
+ SkBitmap::kARGB_8888_Config, width, height, is_opaque));
+}
+#endif
+
+} // namespace skia
diff --git a/skia/ext/platform_canvas_unittest.cc b/skia/ext/platform_canvas_unittest.cc
new file mode 100644
index 0000000000..5679721977
--- /dev/null
+++ b/skia/ext/platform_canvas_unittest.cc
@@ -0,0 +1,398 @@
+// 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 "skia/ext/platform_canvas.h"
+#include "skia/ext/platform_device.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "SkColor.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.
+ PlatformCanvas canvas(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
+ PlatformCanvas canvas(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.
+ PlatformCanvas canvas(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, 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, 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, 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, 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.
+ PlatformCanvas canvas(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, 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, 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, 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, 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, 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)
+
+} // namespace skia
diff --git a/skia/ext/platform_canvas_win.cc b/skia/ext/platform_canvas_win.cc
new file mode 100644
index 0000000000..7b9a787298
--- /dev/null
+++ b/skia/ext/platform_canvas_win.cc
@@ -0,0 +1,40 @@
+// 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 "base/debug/trace_event.h"
+#include "skia/ext/bitmap_platform_device_win.h"
+#include "skia/ext/platform_canvas.h"
+
+namespace skia {
+
+PlatformCanvas::PlatformCanvas(int width, int height, bool is_opaque) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ initialize(width, height, is_opaque, NULL);
+}
+
+PlatformCanvas::PlatformCanvas(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section) {
+ TRACE_EVENT2("skia", "PlatformCanvas::PlatformCanvas",
+ "width", width, "height", height);
+ initialize(width, height, is_opaque, shared_section);
+}
+
+PlatformCanvas::~PlatformCanvas() {
+}
+
+bool PlatformCanvas::initialize(int width,
+ int height,
+ bool is_opaque,
+ HANDLE shared_section) {
+ return initializeWithDevice(BitmapPlatformDevice::Create(
+ width, height, is_opaque, shared_section));
+}
+
+} // namespace skia
diff --git a/skia/ext/platform_device.cc b/skia/ext/platform_device.cc
new file mode 100644
index 0000000000..83a191862d
--- /dev/null
+++ b/skia/ext/platform_device.cc
@@ -0,0 +1,79 @@
+// 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) {
+ 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/skia/ext/platform_device.h b/skia/ext/platform_device.h
new file mode 100644
index 0000000000..69efb3f723
--- /dev/null
+++ b/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_SUN)
+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_SUN)
+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/skia/ext/platform_device_linux.cc b/skia/ext/platform_device_linux.cc
new file mode 100644
index 0000000000..f72e6148c4
--- /dev/null
+++ b/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/skia/ext/platform_device_mac.cc b/skia/ext/platform_device_mac.cc
new file mode 100644
index 0000000000..6ff017d5cd
--- /dev/null
+++ b/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/skia/ext/platform_device_win.cc b/skia/ext/platform_device_win.cc
new file mode 100644
index 0000000000..f44e66eb43
--- /dev/null
+++ b/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/skia/ext/skia_sandbox_support_win.cc b/skia/ext/skia_sandbox_support_win.cc
new file mode 100644
index 0000000000..13efb80fdb
--- /dev/null
+++ b/skia/ext/skia_sandbox_support_win.cc
@@ -0,0 +1,24 @@
+// 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_sandbox_support_win.h"
+#include "SkFontHost.h"
+#include "SkTypeface_win.h"
+
+static SkiaEnsureTypefaceAccessible g_skia_ensure_typeface_accessible = NULL;
+
+SK_API void SetSkiaEnsureTypefaceAccessible(SkiaEnsureTypefaceAccessible func) {
+ // This function is supposed to be called once in process life time.
+ SkASSERT(g_skia_ensure_typeface_accessible == NULL);
+ g_skia_ensure_typeface_accessible = func;
+}
+
+// static
+void SkFontHost::EnsureTypefaceAccessible(const SkTypeface& typeface) {
+ if (g_skia_ensure_typeface_accessible) {
+ LOGFONT lf;
+ SkLOGFONTFromTypeface(&typeface, &lf);
+ g_skia_ensure_typeface_accessible(lf);
+ }
+}
diff --git a/skia/ext/skia_sandbox_support_win.h b/skia/ext/skia_sandbox_support_win.h
new file mode 100644
index 0000000000..47615bf443
--- /dev/null
+++ b/skia/ext/skia_sandbox_support_win.h
@@ -0,0 +1,16 @@
+// 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_SKIA_SANDBOX_SUPPORT_WIN_H_
+#define SKIA_EXT_SKIA_SANDBOX_SUPPORT_WIN_H_
+
+#include <windows.h>
+
+#include "SkPreConfig.h"
+
+typedef void (*SkiaEnsureTypefaceAccessible)(const LOGFONT& font);
+
+SK_API void SetSkiaEnsureTypefaceAccessible(SkiaEnsureTypefaceAccessible func);
+
+#endif // SKIA_EXT_SKIA_SANDBOX_SUPPORT_WIN_H_
diff --git a/skia/ext/skia_trace_shim.h b/skia/ext/skia_trace_shim.h
new file mode 100644
index 0000000000..62bdecc4ce
--- /dev/null
+++ b/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/skia/ext/skia_utils_ios.h b/skia/ext/skia_utils_ios.h
new file mode 100644
index 0000000000..f213cf0f79
--- /dev/null
+++ b/skia/ext/skia_utils_ios.h
@@ -0,0 +1,33 @@
+// 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;
+#else
+class UIImage;
+#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);
+
+} // namespace gfx
+
+#endif // SKIA_EXT_SKIA_UTILS_IOS_H_
diff --git a/skia/ext/skia_utils_ios.mm b/skia/ext/skia_utils_ios.mm
new file mode 100644
index 0000000000..fba12df795
--- /dev/null
+++ b/skia/ext/skia_utils_ios.mm
@@ -0,0 +1,72 @@
+// 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 <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::mac::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateDeviceRGB());
+ base::mac::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);
+ 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::mac::ScopedCFTypeRef<CGImageRef> cg_image(
+ SkCreateCGImageRefWithColorspace(skia_bitmap, color_space));
+
+ // Now convert to UIImage.
+ return [UIImage imageWithCGImage:cg_image.get()
+ scale:scale
+ orientation:UIImageOrientationUp];
+}
+
+} // namespace gfx
diff --git a/skia/ext/skia_utils_mac.h b/skia/ext/skia_utils_mac.h
new file mode 100644
index 0000000000..ad5fc93d70
--- /dev/null
+++ b/skia/ext/skia_utils_mac.h
@@ -0,0 +1,120 @@
+// 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.
+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 to NSColor.
+SK_API NSColor* SkColorToCalibratedNSColor(SkColor color);
+SK_API NSColor* SkColorToDeviceNSColor(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/skia/ext/skia_utils_mac.mm b/skia/ext/skia_utils_mac.mm
new file mode 100644
index 0000000000..e95635b1b8
--- /dev/null
+++ b/skia/ext/skia_utils_mac.mm
@@ -0,0 +1,418 @@
+// 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/memory/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::mac::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::mac::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];
+}
+
+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::mac::ScopedCFTypeRef<CGColorSpaceRef> color_space(
+ CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB));
+ return SkBitmapToNSBitmapImageRepWithColorSpace(skiaBitmap, color_space);
+}
+
+NSBitmapImageRep* SkBitmapToNSBitmapImageRepWithColorSpace(
+ const SkBitmap& skiaBitmap,
+ CGColorSpaceRef colorSpace) {
+ // First convert SkBitmap to CGImageRef.
+ base::mac::ScopedCFTypeRef<CGImageRef> cgimage(
+ SkCreateCGImageRefWithColorspace(skiaBitmap, colorSpace));
+
+ // Now convert to NSBitmapImageRep.
+ scoped_nsobject<NSBitmapImageRep> bitmap(
+ [[NSBitmapImageRep alloc] initWithCGImage:cgimage]);
+ return [bitmap.release() autorelease];
+}
+
+NSImage* SkBitmapToNSImageWithColorSpace(const SkBitmap& skiaBitmap,
+ CGColorSpaceRef colorSpace) {
+ if (skiaBitmap.isNull())
+ return nil;
+
+ // First convert SkBitmap to CGImageRef.
+ base::mac::ScopedCFTypeRef<CGImageRef> cgimage(
+ SkCreateCGImageRefWithColorspace(skiaBitmap, colorSpace));
+
+ // Now convert to NSImage.
+ scoped_nsobject<NSBitmapImageRep> bitmap(
+ [[NSBitmapImageRep alloc] initWithCGImage:cgimage]);
+ scoped_nsobject<NSImage> image([[NSImage alloc] init]);
+ [image addRepresentation:bitmap];
+ [image setSize:NSMakeSize(skiaBitmap.width(), skiaBitmap.height())];
+ return [image.release() autorelease];
+}
+
+NSImage* SkBitmapToNSImage(const SkBitmap& skiaBitmap) {
+ base::mac::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);
+ }
+ cgContext_ = CGBitmapContextCreate(bitmap_.getPixels(), bitmap_.width(),
+ bitmap_.height(), 8, bitmap_.rowBytes(), CGColorSpaceCreateDeviceRGB(),
+ 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/skia/ext/skia_utils_mac_unittest.mm b/skia/ext/skia_utils_mac_unittest.mm
new file mode 100644
index 0000000000..353c52a380
--- /dev/null
+++ b/skia/ext/skia_utils_mac_unittest.mm
@@ -0,0 +1,244 @@
+// 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 "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) {
+ 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/skia/ext/skia_utils_win.cc b/skia/ext/skia_utils_win.cc
new file mode 100644
index 0000000000..4988a3a8c8
--- /dev/null
+++ b/skia/ext/skia_utils_win.cc
@@ -0,0 +1,70 @@
+// 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) {
+ // Currently, Alpha is always 255 or the color is 0 so there is no need to
+ // demultiply the channels. If this DCHECK() is ever hit, the full
+ // (SkColorGetX(color) * 255 / a) will have to be added in the conversion.
+ SkASSERT((0xFF == SkColorGetA(color)) || (0 == 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/skia/ext/skia_utils_win.h b/skia/ext/skia_utils_win.h
new file mode 100644
index 0000000000..98e1b999be
--- /dev/null
+++ b/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/skia/ext/vector_canvas.cc b/skia/ext/vector_canvas.cc
new file mode 100644
index 0000000000..96c23e92c3
--- /dev/null
+++ b/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) {
+ setDevice(device)->unref(); // Created with refcount 1, and setDevice refs.
+}
+
+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/skia/ext/vector_canvas.h b/skia/ext/vector_canvas.h
new file mode 100644
index 0000000000..e7a67fce9d
--- /dev/null
+++ b/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/skia/ext/vector_canvas_unittest.cc b/skia/ext/vector_canvas_unittest.cc
new file mode 100644
index 0000000000..d5f7f41932
--- /dev/null
+++ b/skia/ext/vector_canvas_unittest.cc
@@ -0,0 +1,968 @@
+// 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/string_util.h"
+#include "base/stringprintf.h"
+#include "base/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 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 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.
+ FilePath::StringType tmp(test_dir_.value());
+ for (size_t i = 0; i < tmp.size(); ++i)
+ tmp[i] = base::ToLowerASCII(tmp[i]);
+ test_dir_ = FilePath(tmp);
+
+ if (action_ == GENERATE) {
+ // Make sure the directory exist.
+ file_util::CreateDirectory(test_dir_);
+ }
+ }
+
+ // Returns the fully qualified path of a data file.
+ FilePath test_file(const 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,
+ 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 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 FilePath::StringType& filename) const {
+ Image(canvas).SaveToFile(test_file(filename));
+ }
+
+ ProcessAction action_;
+
+ // Path to directory used to contain the test data.
+ 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 FilePath& filename,
+ SkBitmap* bitmap,
+ bool is_opaque) {
+ std::string compressed;
+ file_util::ReadFileToString(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_ = new PlatformCanvas(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 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 };
+ SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+ 0);
+ paint.setPathEffect(effect)->unref();
+ 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 };
+ SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+ 0);
+ paint.setPathEffect(effect)->unref();
+ 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 };
+ SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+ 0);
+ paint.setPathEffect(effect)->unref();
+ 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 };
+ SkPathEffect* effect = new SkDashPathEffect(intervals, arraysize(intervals),
+ 0);
+ paint.setPathEffect(effect)->unref();
+ 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/skia/ext/vector_platform_device_emf_win.cc b/skia/ext/vector_platform_device_emf_win.cc
new file mode 100644
index 0000000000..73be49119f
--- /dev/null
+++ b/skia/ext/vector_platform_device_emf_win.cc
@@ -0,0 +1,874 @@
+// 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 "skia/ext/vector_platform_device_emf_win.h"
+
+#include "skia/ext/bitmap_platform_device.h"
+#include "skia/ext/skia_utils_win.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);
+ SkSafeUnref(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);
+ SkSafeUnref(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::drawBitmap(const SkDraw& draw,
+ const SkBitmap& bitmap,
+ const SkIRect* srcRectOrNull,
+ 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;
+ }
+}
+
+void VectorPlatformDeviceEmf::drawText(const SkDraw& draw,
+ const void* text,
+ size_t byteLength,
+ SkScalar x,
+ SkScalar y,
+ const SkPaint& paint) {
+ SkGDIFontSetup setup;
+ if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding()
+ && setup.useGDI(hdc_, paint)) {
+ UINT options = getTextOutOptions(paint);
+ UINT count = byteLength >> 1;
+ ExtTextOut(hdc_, SkScalarRound(x), SkScalarRound(y + getAscent(paint)),
+ options, 0, reinterpret_cast<const wchar_t*>(text), count, NULL);
+ } else {
+ 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;
+ 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;
+ }
+ ExtTextOut(hdc_, startX, startY, getTextOutOptions(paint), 0,
+ reinterpret_cast<const wchar_t*>(text), count, advances);
+ } else {
+ 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::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/skia/ext/vector_platform_device_emf_win.h b/skia/ext/vector_platform_device_emf_win.h
new file mode 100644
index 0000000000..6d84d3a8be
--- /dev/null
+++ b/skia/ext/vector_platform_device_emf_win.h
@@ -0,0 +1,132 @@
+// 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 drawBitmap(const SkDraw& draw, const SkBitmap& bitmap,
+ const SkIRect* srcRectOrNull,
+ 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);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_VECTOR_PLATFORM_DEVICE_EMF_WIN_H_
diff --git a/skia/ext/vector_platform_device_skia.cc b/skia/ext/vector_platform_device_skia.cc
new file mode 100644
index 0000000000..14376222b4
--- /dev/null
+++ b/skia/ext/vector_platform_device_skia.cc
@@ -0,0 +1,89 @@
+// 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_ = BitmapPlatformDevice::CreateAndClear(width(), height(),
+ false);
+ raster_surface_->unref(); // SkRefPtr and create both took a reference.
+ 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_ = NULL;
+}
+
+#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/skia/ext/vector_platform_device_skia.h b/skia/ext/vector_platform_device_skia.h
new file mode 100644
index 0000000000..0575563f98
--- /dev/null
+++ b/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 "third_party/skia/include/core/SkRefCnt.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:
+ SkRefPtr<BitmapPlatformDevice> raster_surface_;
+
+ DISALLOW_COPY_AND_ASSIGN(VectorPlatformDeviceSkia);
+};
+
+} // namespace skia
+
+#endif // SKIA_EXT_VECTOR_PLATFORM_DEVICE_SKIA_H_
diff --git a/skia/fix_for_1186198.diff b/skia/fix_for_1186198.diff
new file mode 100644
index 0000000000..6158f594a2
--- /dev/null
+++ b/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/skia/skia.gyp b/skia/skia.gyp
new file mode 100644
index 0000000000..37d6c2e497
--- /dev/null
+++ b/skia/skia.gyp
@@ -0,0 +1,853 @@
+# 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'skia',
+ 'type': '<(component)',
+ 'variables': {
+ 'conditions': [
+ ['OS== "ios"', {
+ 'skia_support_gpu': 0,
+ }, {
+ 'skia_support_gpu': 1,
+ }],
+
+ ['inside_chromium_build==0', {
+ 'webkit_src_dir': '<(DEPTH)/../../..',
+ },{
+ 'webkit_src_dir': '<(DEPTH)/third_party/WebKit',
+ }],
+ ],
+
+ 'optimize': 'max',
+
+ # 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',
+ },
+
+ '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',
+
+ '../third_party/skia/src/images/bmpdecoderhelper.cpp',
+ '../third_party/skia/src/images/bmpdecoderhelper.h',
+ #'../third_party/skia/src/images/SkFDStream.cpp',
+ #'../third_party/skia/src/images/SkFlipPixelRef.cpp',
+ '../third_party/skia/src/images/SkImageDecoder.cpp',
+ '../third_party/skia/src/images/SkImageDecoder_Factory.cpp',
+ #'../third_party/skia/src/images/SkImageDecoder_fpdfemb.cpp',
+ #'../third_party/skia/src/images/SkImageDecoder_libbmp.cpp',
+ #'../third_party/skia/src/images/SkImageDecoder_libgif.cpp',
+ #'../third_party/skia/src/images/SkImageDecoder_libico.cpp',
+ #'../third_party/skia/src/images/SkImageDecoder_libjpeg.cpp',
+ #'../third_party/skia/src/images/SkImageDecoder_libpng.cpp',
+ #'../third_party/skia/src/images/SkImageDecoder_libpvjpeg.cpp',
+ #'../third_party/skia/src/images/SkImageDecoder_wbmp.cpp',
+ #'../third_party/skia/src/images/SkImageEncoder.cpp',
+ #'../third_party/skia/src/images/SkImageEncoder_Factory.cpp',
+ #'../third_party/skia/src/images/SkImageRef.cpp',
+ #'../third_party/skia/src/images/SkImageRefPool.cpp',
+ #'../third_party/skia/src/images/SkImageRefPool.h',
+ #'../third_party/skia/src/images/SkImageRef_GlobalPool.cpp',
+ #'../third_party/skia/src/images/SkMovie.cpp',
+ #'../third_party/skia/src/images/SkMovie_gif.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/SkPDFPage.cpp',
+ '../third_party/skia/src/pdf/SkPDFPage.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/FontHostConfiguration_android.cpp',
+ '../third_party/skia/src/ports/SkFontDescriptor.cpp',
+ '../third_party/skia/src/ports/SkFontDescriptor.h',
+ #'../third_party/skia/src/ports/SkFontHost_FONTPATH.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/SkFontHost_android.cpp',
+ #'../third_party/skia/src/ports/SkFontHost_ascender.cpp',
+ '../third_party/skia/src/ports/SkFontHost_tables.cpp',
+ #'../third_party/skia/src/ports/SkFontHost_linux.cpp',
+ '../third_party/skia/src/ports/SkFontHost_mac.cpp',
+ #'../third_party/skia/src/ports/SkFontHost_none.cpp',
+ '../third_party/skia/src/ports/SkFontHost_sandbox_none.cpp',
+ '../third_party/skia/src/ports/SkFontHost_win.cpp',
+ '../third_party/skia/src/ports/SkGlobalInitialization_chromium.cpp',
+ #'../third_party/skia/src/ports/SkImageDecoder_CG.cpp',
+ #'../third_party/skia/src/ports/SkImageDecoder_empty.cpp',
+ #'../third_party/skia/src/ports/SkImageRef_ashmem.cpp',
+ #'../third_party/skia/src/ports/SkImageRef_ashmem.h',
+ #'../third_party/skia/src/ports/SkOSEvent_android.cpp',
+ #'../third_party/skia/src/ports/SkOSEvent_dummy.cpp',
+ '../third_party/skia/src/ports/SkOSFile_stdio.cpp',
+ #'../third_party/skia/src/ports/SkThread_none.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/SkXMLParser_empty.cpp',
+ #'../third_party/skia/src/ports/SkXMLParser_expat.cpp',
+ #'../third_party/skia/src/ports/SkXMLParser_tinyxml.cpp',
+ #'../third_party/skia/src/ports/SkXMLPullParser_expat.cpp',
+
+ '../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/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/include/pdf/SkPDFDevice.h',
+ '../third_party/skia/include/pdf/SkPDFDocument.h',
+
+ '../third_party/skia/include/ports/SkTypeface_win.h',
+
+ '../third_party/skia/include/images/SkFlipPixelRef.h',
+ '../third_party/skia/include/images/SkImageDecoder.h',
+ '../third_party/skia/include/images/SkImageEncoder.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',
+
+ '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/SkThread_chrome.cc',
+ 'ext/platform_canvas.cc',
+ 'ext/platform_canvas.h',
+ 'ext/platform_canvas_linux.cc',
+ 'ext/platform_canvas_mac.cc',
+ 'ext/platform_canvas_skia.cc',
+ 'ext/platform_canvas_win.cc',
+ 'ext/platform_device.cc',
+ 'ext/platform_device.h',
+ 'ext/platform_device_linux.cc',
+ 'ext/platform_device_mac.cc',
+ 'ext/platform_device_win.cc',
+ 'ext/SkMemory_new_handler.cpp',
+ 'ext/skia_sandbox_support_win.h',
+ 'ext/skia_sandbox_support_win.cc',
+ 'ext/skia_trace_shim.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',
+ ],
+ '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/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/sfnt',
+ '../third_party/skia/src/utils',
+ ],
+ 'msvs_disabled_warnings': [4244, 4267, 4341, 4345, 4390, 4554, 4748, 4800],
+ 'defines': [
+ #'SK_GAMMA_SRGB',
+ #'SK_GAMMA_APPLY_TO_A8',
+ 'SK_BUILD_NO_IMAGE_ENCODE',
+ 'GR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"',
+ 'GR_STATIC_RECT_VB=1',
+ 'GR_AGGRESSIVE_SHADER_OPTS=1',
+ 'SK_DISABLE_FAST_AA_STROKE_RECT',
+ 'SK_DEFERRED_CANVAS_USES_GPIPE=1',
+
+ # 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',
+
+ # Temporarily keep old int-srcrect behavior, until we determine if
+ # the few failures are a bug or not.
+ 'SK_SUPPORT_INT_SRCRECT_DRAWBITMAPRECT',
+ ],
+ 'sources!': [
+ '../third_party/skia/include/core/SkTypes.h',
+ ],
+ 'conditions': [
+ ['skia_support_gpu != 0', {
+ 'includes': [
+ '../third_party/skia/gyp/gpu.gypi',
+ ],
+ 'sources': [
+ '<@(gr_sources)',
+ '<@(skgr_sources)',
+ ],
+ 'include_dirs': [
+ '../third_party/skia/include/gpu',
+ '../third_party/skia/include/gpu/gl',
+ '../third_party/skia/src/gpu',
+ ],
+ }, { # skia_support_gpu == 0
+ 'defines': [
+ 'SK_SUPPORT_GPU=0',
+ ],
+ }],
+ ['release_valgrind_build == 1', {
+ 'defines': [
+ 'SK_DEBUG_PATH_REF=1',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'SK_DEBUG_PATH_REF=1',
+ ],
+ },
+ }],
+ #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" or 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',
+ ],
+ },
+ 'sources!': [
+ 'ext/SkThread_chrome.cc',
+ ],
+ }],
+ [ 'OS != "android"', {
+ 'sources/': [
+ ['exclude', '_android\\.(cc|cpp)$'],
+ ],
+ 'defines': [
+ 'SK_DEFAULT_FONT_CACHE_LIMIT=(20*1024*1024)',
+ ],
+ }],
+ [ 'OS != "ios"', {
+ 'sources/': [
+ ['exclude', '_ios\\.(cc|cpp|mm?)$'],
+ ],
+ 'dependencies': [
+ '<(webkit_src_dir)/Source/WebKit/chromium/skia_webkit.gyp:skia_webkit',
+ ],
+ }],
+ [ 'OS != "mac"', {
+ 'sources/': [
+ ['exclude', '_mac\\.(cc|cpp|mm?)$'],
+ ['exclude', '/mac/']
+ ],
+ }],
+ [ 'OS != "win"', {
+ 'sources/': [ ['exclude', '_win\\.(cc|cpp)$'] ],
+ }],
+ [ 'chromeos == 1', {
+ 'defines': [
+ # Temporarily use SkPaint to keep a scale factor needed for correct
+ # font rendering in high DPI mode.
+ # See https://codereview.appspot.com/6495089/
+ 'SK_SUPPORT_HINTING_SCALE_FACTOR',
+ ],
+ }],
+ [ 'armv7 == 1', {
+ 'defines': [
+ '__ARM_ARCH__=7',
+ ],
+ }],
+ [ 'armv7 == 1 and arm_neon == 1', {
+ 'defines': [
+ '__ARM_HAVE_NEON',
+ ],
+ }],
+ [ '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/harfbuzz/harfbuzz.gyp:harfbuzz',
+ '../third_party/icu/icu.gyp:icuuc',
+ ],
+ 'cflags': [
+ '-Wno-unused',
+ '-Wno-unused-function',
+ ],
+ 'sources': [
+ 'ext/SkFontHost_fontconfig.cpp',
+ 'ext/SkFontHost_fontconfig_direct.cpp',
+ ],
+ 'defines': [
+# 'SK_USE_COLOR_LUMINANCE',
+ ],
+ }],
+ [ 'use_glib == 0 and OS != "android"', {
+ 'sources/': [ ['exclude', '_linux\\.(cc|cpp)$'] ],
+ 'sources!': [
+ '../third_party/skia/src/ports/SkFontHost_FreeType.cpp',
+ '../third_party/skia/src/ports/SkFontHost_FreeType_common.cpp',
+ ],
+ }],
+ [ 'use_aura == 1 and use_canvas_skia == 1', {
+ 'sources/': [
+ ['exclude', 'ext/platform_canvas_mac\\.cc$'],
+ ['exclude', 'ext/platform_canvas_linux\\.cc$'],
+ ['exclude', 'ext/platform_canvas_win\\.cc$'],
+ ],
+ }, { # use_aura == 0 and use_canvas_skia == 1
+ 'sources/': [ ['exclude', 'ext/platform_canvas_skia\\.cc$'] ],
+ }],
+ [ 'toolkit_uses_gtk == 1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:gdk',
+ ],
+ }, { # toolkit_uses_gtk == 0
+ 'sources/': [ ['exclude', '_gtk\\.(cc|cpp)$'] ],
+ }],
+ [ 'OS == "android"', {
+ 'sources/': [
+ ['exclude', '_linux\\.(cc|cpp)$'],
+ ],
+ 'conditions': [
+ [ '_toolset == "target"', {
+ 'defines': [
+ 'HAVE_PTHREADS',
+ 'OS_ANDROID',
+ 'SK_BUILD_FOR_ANDROID_NDK',
+ # Android devices are typically more memory constrained, so
+ # use a smaller glyph cache.
+ 'SK_DEFAULT_FONT_CACHE_LIMIT=(8*1024*1024)',
+ 'USE_CHROMIUM_SKIA',
+ ],
+ 'dependencies': [
+ '../third_party/expat/expat.gyp:expat',
+ '../third_party/freetype/freetype.gyp:ft2',
+ '../third_party/harfbuzz/harfbuzz.gyp:harfbuzz',
+ 'skia_opts'
+ ],
+ 'dependencies!': [
+ # Android doesn't use Skia's PDF generation, which is what uses
+ # sfntly.
+ '../third_party/sfntly/sfntly.gyp:sfntly',
+ ],
+ # 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',
+ ],
+ 'sources/': [
+ ['include', 'ext/platform_device_linux\\.cc$'],
+ ['include', 'ext/platform_canvas_linux\\.cc$'],
+ ['exclude', '../third_party/skia/src/pdf/'],
+ ],
+ 'sources!': [
+ 'ext/vector_platform_device_skia.cc',
+ ],
+ 'export_dependent_settings': [
+ '../third_party/harfbuzz/harfbuzz.gyp:harfbuzz',
+ ],
+ }],
+ [ '_toolset == "target" and android_build_type == 0', {
+ 'defines': [
+ 'HAVE_ENDIAN_H',
+ ],
+ }],
+ [ '_toolset=="host" and host_os=="linux"', {
+ 'sources': [
+ 'ext/platform_device_linux.cc',
+ 'ext/platform_canvas_linux.cc',
+ ],
+ }],
+ ],
+ }],
+ [ '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',
+ ],
+ 'dependencies': [
+ 'skia_opts_ios',
+ ],
+ 'dependencies!': [
+ 'skia_opts',
+ '../third_party/sfntly/sfntly.gyp:sfntly',
+ ],
+ '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', '/pdf/'],
+ ['exclude', '^ext/vector_platform_device_skia\\.'],
+ ['exclude', 'opts_check_SSE2\\.cpp$'],
+ ['exclude', 'SkFontHost_tables\\.cpp$',],
+ ],
+ }],
+ [ 'OS == "mac"', {
+ 'defines': [
+ 'SK_BUILD_FOR_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',
+ ],
+ 'sources!': [
+ # The mac's fonthost implements the table methods natively,
+ # so no need for these generic versions.
+ '../third_party/skia/src/ports/SkFontHost_tables.cpp',
+ ],
+ 'conditions': [
+ [ 'use_skia == 0', {
+ 'sources/': [
+ ['exclude', '/pdf/'],
+ ['exclude', 'ext/vector_platform_device_skia\\.(cc|h)'],
+ ],
+ },
+ { # use_skia
+ 'defines': [
+ 'SK_USE_MAC_CORE_TEXT',
+# 'SK_USE_COLOR_LUMINANCE',
+ ],
+ }],
+ ],
+ }],
+ [ 'OS == "win"', {
+ 'sources!': [
+ '../third_party/skia/src/core/SkMMapStream.cpp',
+ '../third_party/skia/src/ports/SkFontHost_sandbox_none.cpp',
+ '../third_party/skia/src/ports/SkThread_pthread.cpp',
+ '../third_party/skia/src/ports/SkTime_Unix.cpp',
+ 'ext/SkThread_chrome.cc',
+ ],
+ 'include_dirs': [
+ 'config/win',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'config/win',
+ ],
+ },
+ }],
+ ['component=="shared_library"', {
+ 'defines': [
+ 'GR_DLL=1',
+ 'GR_IMPLEMENTATION=1',
+ 'SKIA_DLL',
+ 'SKIA_IMPLEMENTATION=1',
+ ],
+ 'dependencies': [
+ '../base/base.gyp:base',
+ ],
+ 'direct_dependent_settings': {
+ 'defines': [
+ 'GR_DLL',
+ 'SKIA_DLL',
+ ],
+ },
+ }],
+ ],
+ 'dependencies': [
+ 'skia_opts',
+ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
+ '../third_party/sfntly/sfntly.gyp:sfntly',
+ '../third_party/zlib/zlib.gyp:zlib',
+ ],
+ 'direct_dependent_settings': {
+ 'include_dirs': [
+ 'config',
+
+ #temporary until we can hide SkFontHost
+ '../third_party/skia/src/core',
+
+ '../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/gpu/gl',
+ '../third_party/skia/include/pipe',
+ '../third_party/skia/include/ports',
+ '../third_party/skia/include/utils',
+ 'ext',
+ ],
+ 'defines': [
+ 'SK_BUILD_NO_IMAGE_ENCODE',
+ 'SK_DEFERRED_CANVAS_USES_GPIPE=1',
+ 'GR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"',
+ 'GR_AGGRESSIVE_SHADER_OPTS=1',
+ ],
+ 'conditions': [
+ [ 'chromeos == 1', {
+ 'defines': [
+ 'SK_SUPPORT_HINTING_SCALE_FACTOR',
+ ],
+ }],
+ ['OS=="android"', {
+ 'dependencies!': [
+ 'skia_opts',
+ '../third_party/zlib/zlib.gyp:zlib',
+ ],
+ 'defines': [
+ # Don't use non-NDK available stuff.
+ 'SK_BUILD_FOR_ANDROID_NDK',
+ ],
+ 'conditions': [
+ [ '_toolset == "target" and android_build_type == 0', {
+ 'defines': [
+ 'HAVE_ENDIAN_H',
+ ],
+ }],
+ ],
+ }],
+ ['OS=="mac"', {
+ 'include_dirs': [
+ '../third_party/skia/include/utils/mac',
+ ],
+ }],
+ ],
+ },
+ '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$',],
+ ],
+ }],
+ ],
+ },
+
+ # 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',
+ 'variables': {
+ 'optimize': 'max',
+ },
+ '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/utils',
+ '../third_party/skia/src/core',
+ ],
+ 'conditions': [
+ [ 'os_posix == 1 and OS != "mac" and OS != "android" and \
+ target_arch != "arm" and target_arch != "mipsel"', {
+ 'cflags': [
+ '-msse2',
+ ],
+ }],
+ [ 'OS == "android"', {
+ 'defines': [
+ 'SK_BUILD_FOR_ANDROID_NDK',
+ ],
+ }],
+ [ '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',
+ ],
+ 'conditions': [
+ # x86 Android doesn't support SSSE3 instructions.
+ [ 'OS != "android"', {
+ 'dependencies': [
+ 'skia_opts_ssse3',
+ ],
+ }],
+ ],
+ }],
+ [ 'target_arch == "arm"', {
+ 'conditions': [
+ [ 'armv7 == 1', {
+ 'defines': [
+ '__ARM_ARCH__=7',
+ ],
+ }],
+ [ 'armv7 == 1 and arm_neon == 1', {
+ 'defines': [
+ '__ARM_HAVE_NEON',
+ ],
+ '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',
+ ],
+ }],
+ ],
+ # 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',
+ ],
+ 'cflags': [
+ '-fomit-frame-pointer',
+ ],
+ 'sources': [
+ '../third_party/skia/src/opts/SkBitmapProcState_opts_arm.cpp',
+ ],
+ }],
+ [ 'armv7 == 1 and arm_neon == 0', {
+ 'sources': [
+ '../third_party/skia/src/opts/memset.arm.S',
+ ],
+ }],
+ [ 'armv7 == 1 and arm_neon == 1', {
+ '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',
+ ],
+ }],
+ [ 'target_arch == "arm" and armv7 == 0', {
+ 'sources': [
+ '../third_party/skia/src/opts/SkBlitRow_opts_none.cpp',
+ '../third_party/skia/src/opts/SkUtils_opts_none.cpp',
+ ],
+ }],
+ [ 'target_arch == "arm" and armv7 == 1', {
+ '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',
+ 'variables': {
+ 'optimize': 'max',
+ },
+ '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"]', {
+ '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"', {
+ 'sources': [
+ '../third_party/skia/src/opts/SkBitmapProcState_opts_SSSE3.cpp',
+ ],
+ }],
+ ],
+ },
+ {
+ 'target_name': 'image_operations_bench',
+ 'type': 'executable',
+ 'dependencies': [
+ '../base/base.gyp:base',
+ 'skia',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'ext/image_operations_bench.cc',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['OS=="ios"', {
+ 'targets': [
+ # 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.
+ {
+ 'target_name': 'skia_opts_ios',
+ 'type': 'static_library',
+ '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/utils',
+ '../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',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/skia/skia.target.mk b/skia/skia.target.mk
new file mode 100644
index 0000000000..45a7acf15c
--- /dev/null
+++ b/skia/skia.target.mk
@@ -0,0 +1,490 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := skia_skia_gyp
+LOCAL_MODULE_SUFFIX := .a
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES := \
+ $(call intermediates-dir-for,GYP,third_party_WebKit_Source_WebKit_chromium_skia_webkit_gyp)/skia_webkit.stamp \
+ $(call intermediates-dir-for,GYP,third_party_expat_expat_gyp)/expat.stamp
+
+GYP_GENERATED_OUTPUTS :=
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+$(gyp_intermediate_dir)/bitmap_platform_device_android.cpp: $(LOCAL_PATH)/skia/ext/bitmap_platform_device_android.cc
+ mkdir -p $(@D); cp $< $@
+$(gyp_intermediate_dir)/convolver.cpp: $(LOCAL_PATH)/skia/ext/convolver.cc
+ mkdir -p $(@D); cp $< $@
+$(gyp_intermediate_dir)/google_logging.cpp: $(LOCAL_PATH)/skia/ext/google_logging.cc
+ mkdir -p $(@D); cp $< $@
+$(gyp_intermediate_dir)/image_operations.cpp: $(LOCAL_PATH)/skia/ext/image_operations.cc
+ mkdir -p $(@D); cp $< $@
+$(gyp_intermediate_dir)/platform_canvas.cpp: $(LOCAL_PATH)/skia/ext/platform_canvas.cc
+ mkdir -p $(@D); cp $< $@
+$(gyp_intermediate_dir)/platform_canvas_linux.cpp: $(LOCAL_PATH)/skia/ext/platform_canvas_linux.cc
+ mkdir -p $(@D); cp $< $@
+$(gyp_intermediate_dir)/platform_device.cpp: $(LOCAL_PATH)/skia/ext/platform_device.cc
+ mkdir -p $(@D); cp $< $@
+$(gyp_intermediate_dir)/platform_device_linux.cpp: $(LOCAL_PATH)/skia/ext/platform_device_linux.cc
+ mkdir -p $(@D); cp $< $@
+$(gyp_intermediate_dir)/vector_canvas.cpp: $(LOCAL_PATH)/skia/ext/vector_canvas.cc
+ mkdir -p $(@D); cp $< $@
+LOCAL_GENERATED_SOURCES := \
+ $(gyp_intermediate_dir)/bitmap_platform_device_android.cpp \
+ $(gyp_intermediate_dir)/convolver.cpp \
+ $(gyp_intermediate_dir)/google_logging.cpp \
+ $(gyp_intermediate_dir)/image_operations.cpp \
+ $(gyp_intermediate_dir)/platform_canvas.cpp \
+ $(gyp_intermediate_dir)/platform_canvas_linux.cpp \
+ $(gyp_intermediate_dir)/platform_device.cpp \
+ $(gyp_intermediate_dir)/platform_device_linux.cpp \
+ $(gyp_intermediate_dir)/vector_canvas.cpp
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS := \
+ $(LOCAL_PATH)/skia/ext
+
+LOCAL_SRC_FILES := \
+ third_party/skia/src/core/SkFlate.cpp \
+ third_party/skia/src/images/bmpdecoderhelper.cpp \
+ third_party/skia/src/images/SkImageDecoder.cpp \
+ third_party/skia/src/images/SkImageDecoder_Factory.cpp \
+ third_party/skia/src/images/SkScaledBitmapSampler.cpp \
+ third_party/skia/src/ports/FontHostConfiguration_android.cpp \
+ third_party/skia/src/ports/SkFontDescriptor.cpp \
+ third_party/skia/src/ports/SkFontHost_FreeType.cpp \
+ third_party/skia/src/ports/SkFontHost_FreeType_common.cpp \
+ third_party/skia/src/ports/SkFontHost_android.cpp \
+ third_party/skia/src/ports/SkFontHost_tables.cpp \
+ third_party/skia/src/ports/SkFontHost_sandbox_none.cpp \
+ third_party/skia/src/ports/SkGlobalInitialization_chromium.cpp \
+ third_party/skia/src/ports/SkOSFile_stdio.cpp \
+ third_party/skia/src/ports/SkThread_pthread.cpp \
+ third_party/skia/src/ports/SkTime_Unix.cpp \
+ third_party/skia/src/sfnt/SkOTUtils.cpp \
+ third_party/skia/src/utils/SkBase64.cpp \
+ third_party/skia/src/utils/SkBitSet.cpp \
+ third_party/skia/src/utils/SkDeferredCanvas.cpp \
+ third_party/skia/src/utils/SkMatrix44.cpp \
+ third_party/skia/src/utils/SkNullCanvas.cpp \
+ third_party/skia/src/utils/SkNWayCanvas.cpp \
+ skia/ext/SkMemory_new_handler.cpp \
+ third_party/skia/src/core/Sk64.cpp \
+ third_party/skia/src/core/SkAAClip.cpp \
+ third_party/skia/src/core/SkAnnotation.cpp \
+ third_party/skia/src/core/SkAdvancedTypefaceMetrics.cpp \
+ third_party/skia/src/core/SkAlphaRuns.cpp \
+ third_party/skia/src/core/SkBBoxHierarchy.cpp \
+ third_party/skia/src/core/SkBBoxRecord.cpp \
+ third_party/skia/src/core/SkBBoxHierarchyRecord.cpp \
+ third_party/skia/src/core/SkBitmap.cpp \
+ third_party/skia/src/core/SkBitmapHeap.cpp \
+ third_party/skia/src/core/SkBitmapProcShader.cpp \
+ third_party/skia/src/core/SkBitmapProcState.cpp \
+ third_party/skia/src/core/SkBitmapProcState_matrixProcs.cpp \
+ third_party/skia/src/core/SkBitmapSampler.cpp \
+ third_party/skia/src/core/SkBitmap_scroll.cpp \
+ third_party/skia/src/core/SkBlitMask_D32.cpp \
+ third_party/skia/src/core/SkBlitRow_D16.cpp \
+ third_party/skia/src/core/SkBlitRow_D32.cpp \
+ third_party/skia/src/core/SkBlitRow_D4444.cpp \
+ third_party/skia/src/core/SkBlitter.cpp \
+ third_party/skia/src/core/SkBlitter_4444.cpp \
+ third_party/skia/src/core/SkBlitter_A1.cpp \
+ third_party/skia/src/core/SkBlitter_A8.cpp \
+ third_party/skia/src/core/SkBlitter_ARGB32.cpp \
+ third_party/skia/src/core/SkBlitter_RGB16.cpp \
+ third_party/skia/src/core/SkBlitter_Sprite.cpp \
+ third_party/skia/src/core/SkBuffer.cpp \
+ third_party/skia/src/core/SkCanvas.cpp \
+ third_party/skia/src/core/SkChunkAlloc.cpp \
+ third_party/skia/src/core/SkClipStack.cpp \
+ third_party/skia/src/core/SkColor.cpp \
+ third_party/skia/src/core/SkColorFilter.cpp \
+ third_party/skia/src/core/SkColorTable.cpp \
+ third_party/skia/src/core/SkComposeShader.cpp \
+ third_party/skia/src/core/SkConcaveToTriangles.cpp \
+ third_party/skia/src/core/SkConfig8888.cpp \
+ third_party/skia/src/core/SkCordic.cpp \
+ third_party/skia/src/core/SkCubicClipper.cpp \
+ third_party/skia/src/core/SkData.cpp \
+ third_party/skia/src/core/SkDebug.cpp \
+ third_party/skia/src/core/SkDeque.cpp \
+ third_party/skia/src/core/SkDevice.cpp \
+ third_party/skia/src/core/SkDeviceProfile.cpp \
+ third_party/skia/src/core/SkDither.cpp \
+ third_party/skia/src/core/SkDraw.cpp \
+ third_party/skia/src/core/SkEdgeBuilder.cpp \
+ third_party/skia/src/core/SkEdgeClipper.cpp \
+ third_party/skia/src/core/SkEdge.cpp \
+ third_party/skia/src/core/SkFilterProc.cpp \
+ third_party/skia/src/core/SkFlattenable.cpp \
+ third_party/skia/src/core/SkFlattenableBuffers.cpp \
+ third_party/skia/src/core/SkFloat.cpp \
+ third_party/skia/src/core/SkFloatBits.cpp \
+ third_party/skia/src/core/SkFontHost.cpp \
+ third_party/skia/src/core/SkGeometry.cpp \
+ third_party/skia/src/core/SkGlyphCache.cpp \
+ third_party/skia/src/core/SkGraphics.cpp \
+ third_party/skia/src/core/SkInstCnt.cpp \
+ third_party/skia/src/core/SkImageFilter.cpp \
+ third_party/skia/src/core/SkLineClipper.cpp \
+ third_party/skia/src/core/SkMallocPixelRef.cpp \
+ third_party/skia/src/core/SkMask.cpp \
+ third_party/skia/src/core/SkMaskFilter.cpp \
+ third_party/skia/src/core/SkMaskGamma.cpp \
+ third_party/skia/src/core/SkMath.cpp \
+ third_party/skia/src/core/SkMatrix.cpp \
+ third_party/skia/src/core/SkMetaData.cpp \
+ third_party/skia/src/core/SkMMapStream.cpp \
+ third_party/skia/src/core/SkOrderedReadBuffer.cpp \
+ third_party/skia/src/core/SkOrderedWriteBuffer.cpp \
+ third_party/skia/src/core/SkPackBits.cpp \
+ third_party/skia/src/core/SkPaint.cpp \
+ third_party/skia/src/core/SkPath.cpp \
+ third_party/skia/src/core/SkPathEffect.cpp \
+ third_party/skia/src/core/SkPathHeap.cpp \
+ third_party/skia/src/core/SkPathMeasure.cpp \
+ third_party/skia/src/core/SkPicture.cpp \
+ third_party/skia/src/core/SkPictureFlat.cpp \
+ third_party/skia/src/core/SkPicturePlayback.cpp \
+ third_party/skia/src/core/SkPictureRecord.cpp \
+ third_party/skia/src/core/SkPictureStateTree.cpp \
+ third_party/skia/src/core/SkPixelRef.cpp \
+ third_party/skia/src/core/SkPoint.cpp \
+ third_party/skia/src/core/SkProcSpriteBlitter.cpp \
+ third_party/skia/src/core/SkPtrRecorder.cpp \
+ third_party/skia/src/core/SkQuadClipper.cpp \
+ third_party/skia/src/core/SkRasterClip.cpp \
+ third_party/skia/src/core/SkRasterizer.cpp \
+ third_party/skia/src/core/SkRect.cpp \
+ third_party/skia/src/core/SkRefCnt.cpp \
+ third_party/skia/src/core/SkRefDict.cpp \
+ third_party/skia/src/core/SkRegion.cpp \
+ third_party/skia/src/core/SkRegion_path.cpp \
+ third_party/skia/src/core/SkRTree.cpp \
+ third_party/skia/src/core/SkScalar.cpp \
+ third_party/skia/src/core/SkScalerContext.cpp \
+ third_party/skia/src/core/SkScan.cpp \
+ third_party/skia/src/core/SkScan_AntiPath.cpp \
+ third_party/skia/src/core/SkScan_Antihair.cpp \
+ third_party/skia/src/core/SkScan_Hairline.cpp \
+ third_party/skia/src/core/SkScan_Path.cpp \
+ third_party/skia/src/core/SkShader.cpp \
+ third_party/skia/src/core/SkSpriteBlitter_ARGB32.cpp \
+ third_party/skia/src/core/SkSpriteBlitter_RGB16.cpp \
+ third_party/skia/src/core/SkStream.cpp \
+ third_party/skia/src/core/SkString.cpp \
+ third_party/skia/src/core/SkStroke.cpp \
+ third_party/skia/src/core/SkStrokerPriv.cpp \
+ third_party/skia/src/core/SkTileGrid.cpp \
+ third_party/skia/src/core/SkTLS.cpp \
+ third_party/skia/src/core/SkTSearch.cpp \
+ third_party/skia/src/core/SkTypeface.cpp \
+ third_party/skia/src/core/SkTypefaceCache.cpp \
+ third_party/skia/src/core/SkUnPreMultiply.cpp \
+ third_party/skia/src/core/SkUtils.cpp \
+ third_party/skia/src/core/SkWriter32.cpp \
+ third_party/skia/src/core/SkXfermode.cpp \
+ third_party/skia/src/image/SkDataPixelRef.cpp \
+ third_party/skia/src/image/SkImage.cpp \
+ third_party/skia/src/image/SkImagePriv.cpp \
+ third_party/skia/src/image/SkImage_Codec.cpp \
+ third_party/skia/src/image/SkImage_Picture.cpp \
+ third_party/skia/src/image/SkImage_Raster.cpp \
+ third_party/skia/src/image/SkSurface.cpp \
+ third_party/skia/src/image/SkSurface_Picture.cpp \
+ third_party/skia/src/image/SkSurface_Raster.cpp \
+ third_party/skia/src/pipe/SkGPipeRead.cpp \
+ third_party/skia/src/pipe/SkGPipeWrite.cpp \
+ third_party/skia/src/effects/Sk1DPathEffect.cpp \
+ third_party/skia/src/effects/Sk2DPathEffect.cpp \
+ third_party/skia/src/effects/SkAvoidXfermode.cpp \
+ third_party/skia/src/effects/SkArithmeticMode.cpp \
+ third_party/skia/src/effects/SkBitmapSource.cpp \
+ third_party/skia/src/effects/SkBlendImageFilter.cpp \
+ third_party/skia/src/effects/SkBlurDrawLooper.cpp \
+ third_party/skia/src/effects/SkBlurMask.cpp \
+ third_party/skia/src/effects/SkBlurImageFilter.cpp \
+ third_party/skia/src/effects/SkBlurMaskFilter.cpp \
+ third_party/skia/src/effects/SkColorFilters.cpp \
+ third_party/skia/src/effects/SkColorFilterImageFilter.cpp \
+ third_party/skia/src/effects/SkColorMatrix.cpp \
+ third_party/skia/src/effects/SkColorMatrixFilter.cpp \
+ third_party/skia/src/effects/SkCornerPathEffect.cpp \
+ third_party/skia/src/effects/SkDashPathEffect.cpp \
+ third_party/skia/src/effects/SkDiscretePathEffect.cpp \
+ third_party/skia/src/effects/SkEmbossMask.cpp \
+ third_party/skia/src/effects/SkEmbossMaskFilter.cpp \
+ third_party/skia/src/effects/SkKernel33MaskFilter.cpp \
+ third_party/skia/src/effects/SkLayerDrawLooper.cpp \
+ third_party/skia/src/effects/SkLayerRasterizer.cpp \
+ third_party/skia/src/effects/SkLightingImageFilter.cpp \
+ third_party/skia/src/effects/SkOffsetImageFilter.cpp \
+ third_party/skia/src/effects/SkMatrixConvolutionImageFilter.cpp \
+ third_party/skia/src/effects/SkMorphologyImageFilter.cpp \
+ third_party/skia/src/effects/SkPaintFlagsDrawFilter.cpp \
+ third_party/skia/src/effects/SkPixelXorXfermode.cpp \
+ third_party/skia/src/effects/SkPorterDuff.cpp \
+ third_party/skia/src/effects/SkSingleInputImageFilter.cpp \
+ third_party/skia/src/effects/SkStippleMaskFilter.cpp \
+ third_party/skia/src/effects/SkTableColorFilter.cpp \
+ third_party/skia/src/effects/SkTableMaskFilter.cpp \
+ third_party/skia/src/effects/SkTestImageFilters.cpp \
+ third_party/skia/src/effects/SkTransparentShader.cpp \
+ third_party/skia/src/effects/SkMagnifierImageFilter.cpp \
+ third_party/skia/src/effects/gradients/SkBitmapCache.cpp \
+ third_party/skia/src/effects/gradients/SkClampRange.cpp \
+ third_party/skia/src/effects/gradients/SkGradientShader.cpp \
+ third_party/skia/src/effects/gradients/SkLinearGradient.cpp \
+ third_party/skia/src/effects/gradients/SkRadialGradient.cpp \
+ third_party/skia/src/effects/gradients/SkTwoPointRadialGradient.cpp \
+ third_party/skia/src/effects/gradients/SkTwoPointConicalGradient.cpp \
+ third_party/skia/src/effects/gradients/SkSweepGradient.cpp \
+ third_party/skia/src/gpu/GrAAHairLinePathRenderer.cpp \
+ third_party/skia/src/gpu/GrAAConvexPathRenderer.cpp \
+ third_party/skia/src/gpu/GrAARectRenderer.cpp \
+ third_party/skia/src/gpu/GrAddPathRenderers_default.cpp \
+ third_party/skia/src/gpu/GrAllocPool.cpp \
+ third_party/skia/src/gpu/GrAtlas.cpp \
+ third_party/skia/src/gpu/GrBufferAllocPool.cpp \
+ third_party/skia/src/gpu/GrCacheID.cpp \
+ third_party/skia/src/gpu/GrClipData.cpp \
+ third_party/skia/src/gpu/GrContext.cpp \
+ third_party/skia/src/gpu/GrDefaultPathRenderer.cpp \
+ third_party/skia/src/gpu/GrDrawState.cpp \
+ third_party/skia/src/gpu/GrDrawTarget.cpp \
+ third_party/skia/src/gpu/GrEffect.cpp \
+ third_party/skia/src/gpu/GrGeometryBuffer.cpp \
+ third_party/skia/src/gpu/GrClipMaskCache.cpp \
+ third_party/skia/src/gpu/GrClipMaskManager.cpp \
+ third_party/skia/src/gpu/GrGpu.cpp \
+ third_party/skia/src/gpu/GrGpuFactory.cpp \
+ third_party/skia/src/gpu/GrInOrderDrawBuffer.cpp \
+ third_party/skia/src/gpu/GrMemory.cpp \
+ third_party/skia/src/gpu/GrMemoryPool.cpp \
+ third_party/skia/src/gpu/GrPath.cpp \
+ third_party/skia/src/gpu/GrPathRendererChain.cpp \
+ third_party/skia/src/gpu/GrPathRenderer.cpp \
+ third_party/skia/src/gpu/GrPathUtils.cpp \
+ third_party/skia/src/gpu/GrRectanizer.cpp \
+ third_party/skia/src/gpu/GrRenderTarget.cpp \
+ third_party/skia/src/gpu/GrResource.cpp \
+ third_party/skia/src/gpu/GrResourceCache.cpp \
+ third_party/skia/src/gpu/GrStencil.cpp \
+ third_party/skia/src/gpu/GrStencilAndCoverPathRenderer.cpp \
+ third_party/skia/src/gpu/GrStencilBuffer.cpp \
+ third_party/skia/src/gpu/GrSWMaskHelper.cpp \
+ third_party/skia/src/gpu/GrSoftwarePathRenderer.cpp \
+ third_party/skia/src/gpu/GrSurface.cpp \
+ third_party/skia/src/gpu/GrTextContext.cpp \
+ third_party/skia/src/gpu/GrTextStrike.cpp \
+ third_party/skia/src/gpu/GrTexture.cpp \
+ third_party/skia/src/gpu/GrTextureAccess.cpp \
+ third_party/skia/src/gpu/gr_unittests.cpp \
+ third_party/skia/src/gpu/effects/GrTextureStripAtlas.cpp \
+ third_party/skia/src/gpu/effects/GrConfigConversionEffect.cpp \
+ third_party/skia/src/gpu/effects/GrConvolutionEffect.cpp \
+ third_party/skia/src/gpu/effects/GrSingleTextureEffect.cpp \
+ third_party/skia/src/gpu/effects/GrTextureDomainEffect.cpp \
+ third_party/skia/src/gpu/gl/GrGLCaps.cpp \
+ third_party/skia/src/gpu/gl/GrGLContextInfo.cpp \
+ third_party/skia/src/gpu/gl/GrGLCreateNativeInterface_none.cpp \
+ third_party/skia/src/gpu/gl/GrGLDefaultInterface_none.cpp \
+ third_party/skia/src/gpu/gl/GrGLEffect.cpp \
+ third_party/skia/src/gpu/gl/GrGLEffectMatrix.cpp \
+ third_party/skia/src/gpu/gl/GrGLIndexBuffer.cpp \
+ third_party/skia/src/gpu/gl/GrGLInterface.cpp \
+ third_party/skia/src/gpu/gl/GrGLPath.cpp \
+ third_party/skia/src/gpu/gl/GrGLProgram.cpp \
+ third_party/skia/src/gpu/gl/GrGLRenderTarget.cpp \
+ third_party/skia/src/gpu/gl/GrGLShaderBuilder.cpp \
+ third_party/skia/src/gpu/gl/GrGLSL.cpp \
+ third_party/skia/src/gpu/gl/GrGLStencilBuffer.cpp \
+ third_party/skia/src/gpu/gl/GrGLTexture.cpp \
+ third_party/skia/src/gpu/gl/GrGLUtil.cpp \
+ third_party/skia/src/gpu/gl/GrGLUniformManager.cpp \
+ third_party/skia/src/gpu/gl/GrGLVertexBuffer.cpp \
+ third_party/skia/src/gpu/gl/GrGpuGL.cpp \
+ third_party/skia/src/gpu/gl/GrGpuGL_program.cpp \
+ third_party/skia/src/gpu/SkGpuDevice.cpp \
+ third_party/skia/src/gpu/SkGr.cpp \
+ third_party/skia/src/gpu/SkGrFontScaler.cpp \
+ third_party/skia/src/gpu/SkGrPixelRef.cpp \
+ third_party/skia/src/gpu/SkGrTexturePixelRef.cpp \
+ third_party/skia/src/image/SkImage_Gpu.cpp \
+ third_party/skia/src/image/SkSurface_Gpu.cpp \
+ third_party/skia/src/gpu/gl/SkGLContext.cpp
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+ -fno-exceptions \
+ -fno-strict-aliasing \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -fvisibility=hidden \
+ -pipe \
+ -fPIC \
+ -Wno-format \
+ -mthumb \
+ -march=armv7-a \
+ -mtune=cortex-a8 \
+ -mfloat-abi=softfp \
+ -mfpu=vfpv3-d16 \
+ -fno-tree-sra \
+ -fuse-ld=gold \
+ -Wno-psabi \
+ -mthumb-interwork \
+ -ffunction-sections \
+ -funwind-tables \
+ -g \
+ -fstack-protector \
+ -fno-short-enums \
+ -finline-limit=64 \
+ -Wa,--noexecstack \
+ -Wno-error=extra \
+ -Wno-error=ignored-qualifiers \
+ -Wno-error=type-limits \
+ -Wno-error=non-virtual-dtor \
+ -Wno-error=sign-promo \
+ -Wno-error=address \
+ -Wno-error=format-security \
+ -Wno-error=non-virtual-dtor \
+ -Wno-error=return-type \
+ -Wno-error=sequence-point \
+ -Os \
+ -g \
+ -fomit-frame-pointer \
+ -fdata-sections \
+ -ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+ '-D_FILE_OFFSET_BITS=64' \
+ '-DNO_TCMALLOC' \
+ '-DDISABLE_NACL' \
+ '-DCHROMIUM_BUILD' \
+ '-DUSE_LIBJPEG_TURBO=1' \
+ '-DUSE_PROPRIETARY_CODECS' \
+ '-DENABLE_PEPPER_THREADING' \
+ '-DENABLE_GPU=1' \
+ '-DUSE_OPENSSL=1' \
+ '-DENABLE_EGLIMAGE=1' \
+ '-DUSE_SKIA=1' \
+ '-DSK_USE_POSIX_THREADS' \
+ '-DSK_BUILD_NO_IMAGE_ENCODE' \
+ '-DGR_GL_CUSTOM_SETUP_HEADER="GrGLConfig_chrome.h"' \
+ '-DGR_STATIC_RECT_VB=1' \
+ '-DGR_AGGRESSIVE_SHADER_OPTS=1' \
+ '-DSK_DISABLE_FAST_AA_STROKE_RECT' \
+ '-DSK_DEFERRED_CANVAS_USES_GPIPE=1' \
+ '-DSK_ALLOW_OVER_32K_BITMAPS' \
+ '-DSK_ALLOW_STATIC_GLOBAL_INITIALIZERS=0' \
+ '-DSK_SUPPORT_INT_SRCRECT_DRAWBITMAPRECT' \
+ '-DSK_GAMMA_SRGB' \
+ '-DSK_GAMMA_CONTRAST=0.5' \
+ '-D__ARM_ARCH__=7' \
+ '-DHAVE_PTHREADS' \
+ '-DOS_ANDROID' \
+ '-DSK_BUILD_FOR_ANDROID_NDK' \
+ '-DSK_DEFAULT_FONT_CACHE_LIMIT=(8*1024*1024)' \
+ '-DUSE_CHROMIUM_SKIA' \
+ '-DSK_SIMPLE_TWOCOLOR_VERTICAL_GRADIENTS' \
+ '-DSK_DRAW_POS_TEXT_IGNORE_SUBPIXEL_LEFT_ALIGN_FIX' \
+ '-DSK_USE_LEGACY_AA_COVERAGE' \
+ '-DANDROID' \
+ '-D__GNU_SOURCE=1' \
+ '-DUSE_STLPORT=1' \
+ '-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+ '-DCHROME_SYMBOLS_ID=""' \
+ '-DANDROID_UPSTREAM_BRINGUP=1' \
+ '-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+ '-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+ '-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/skia/config \
+ $(LOCAL_PATH)/third_party/skia/include/config \
+ $(LOCAL_PATH)/third_party/skia/include/core \
+ $(LOCAL_PATH)/third_party/skia/include/effects \
+ $(LOCAL_PATH)/third_party/skia/include/images \
+ $(LOCAL_PATH)/third_party/skia/include/pdf \
+ $(LOCAL_PATH)/third_party/skia/include/pipe \
+ $(LOCAL_PATH)/third_party/skia/include/ports \
+ $(LOCAL_PATH)/third_party/skia/include/utils \
+ $(LOCAL_PATH)/third_party/skia/src/core \
+ $(LOCAL_PATH)/third_party/skia/src/image \
+ $(LOCAL_PATH)/third_party/skia/src/sfnt \
+ $(LOCAL_PATH)/third_party/skia/src/utils \
+ $(LOCAL_PATH)/third_party/skia/include/gpu \
+ $(LOCAL_PATH)/third_party/skia/include/gpu/gl \
+ $(LOCAL_PATH)/third_party/skia/src/gpu \
+ $(LOCAL_PATH)/third_party/expat/files/lib \
+ $(LOCAL_PATH)/third_party/zlib \
+ $(GYP_ABS_ANDROID_TOP_DIR)/external/expat/lib \
+ $(LOCAL_PATH)/third_party/freetype/include \
+ $(LOCAL_PATH)/third_party/harfbuzz/contrib \
+ $(LOCAL_PATH)/third_party/harfbuzz/src \
+ $(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
+ $(GYP_ABS_ANDROID_TOP_DIR)/bionic \
+ $(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+ -fno-rtti \
+ -fno-threadsafe-statics \
+ -fvisibility-inlines-hidden \
+ -Wno-deprecated \
+ -Wno-abi \
+ -Wno-error=c++0x-compat
+
+### Rules for final target.
+
+LOCAL_LDFLAGS := \
+ -Wl,-z,noexecstack \
+ -fPIC \
+ -Wl,-z,relro \
+ -Wl,-z,now \
+ -fuse-ld=gold \
+ -nostdlib \
+ -Wl,--no-undefined \
+ -Wl,--exclude-libs=ALL \
+ -Wl,--icf=safe \
+ -Wl,-O1 \
+ -Wl,--as-needed \
+ -Wl,--gc-sections
+
+
+LOCAL_STATIC_LIBRARIES :=
+
+# Enable grouping to fix circular references
+LOCAL_GROUP_STATIC_LIBRARIES := true
+
+LOCAL_SHARED_LIBRARIES := \
+ libstlport \
+ libdl
+
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: skia_skia_gyp
+
+# Alias gyp target name.
+.PHONY: skia
+skia: skia_skia_gyp
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/skia/skia_Prefix.pch b/skia/skia_Prefix.pch
new file mode 100644
index 0000000000..5541dea063
--- /dev/null
+++ b/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/skia/skia_opts.target.mk b/skia/skia_opts.target.mk
new file mode 100644
index 0000000000..ecd474d9a6
--- /dev/null
+++ b/skia/skia_opts.target.mk
@@ -0,0 +1,161 @@
+# This file is generated by gyp; do not edit.
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := skia_skia_opts_gyp
+LOCAL_MODULE_SUFFIX := .a
+LOCAL_MODULE_TAGS := optional
+gyp_intermediate_dir := $(call local-intermediates-dir)
+gyp_shared_intermediate_dir := $(call intermediates-dir-for,GYP,shared)
+
+# Make sure our deps are built first.
+GYP_TARGET_DEPENDENCIES :=
+
+GYP_GENERATED_OUTPUTS :=
+
+# Make sure our deps and generated files are built first.
+LOCAL_ADDITIONAL_DEPENDENCIES := $(GYP_TARGET_DEPENDENCIES) $(GYP_GENERATED_OUTPUTS)
+
+LOCAL_GENERATED_SOURCES :=
+
+GYP_COPIED_SOURCE_ORIGIN_DIRS :=
+
+LOCAL_SRC_FILES := \
+ third_party/skia/src/opts/SkBitmapProcState_opts_arm.cpp \
+ third_party/skia/src/opts/memset.arm.S \
+ third_party/skia/src/opts/SkBlitRow_opts_arm.cpp \
+ third_party/skia/src/opts/opts_check_arm.cpp
+
+
+# Flags passed to both C and C++ files.
+MY_CFLAGS := \
+ -fno-exceptions \
+ -fno-strict-aliasing \
+ -Wno-unused-parameter \
+ -Wno-missing-field-initializers \
+ -fvisibility=hidden \
+ -pipe \
+ -fPIC \
+ -fomit-frame-pointer \
+ -Wno-format \
+ -mthumb \
+ -march=armv7-a \
+ -mtune=cortex-a8 \
+ -mfloat-abi=softfp \
+ -mfpu=vfpv3-d16 \
+ -fno-tree-sra \
+ -fuse-ld=gold \
+ -Wno-psabi \
+ -mthumb-interwork \
+ -ffunction-sections \
+ -funwind-tables \
+ -g \
+ -fstack-protector \
+ -fno-short-enums \
+ -finline-limit=64 \
+ -Wa,--noexecstack \
+ -Wno-error=extra \
+ -Wno-error=ignored-qualifiers \
+ -Wno-error=type-limits \
+ -Wno-error=non-virtual-dtor \
+ -Wno-error=sign-promo \
+ -Wno-error=address \
+ -Wno-error=format-security \
+ -Wno-error=non-virtual-dtor \
+ -Wno-error=return-type \
+ -Wno-error=sequence-point \
+ -Os \
+ -g \
+ -fomit-frame-pointer \
+ -fdata-sections \
+ -ffunction-sections
+
+MY_CFLAGS_C :=
+
+MY_DEFS := \
+ '-D_FILE_OFFSET_BITS=64' \
+ '-DNO_TCMALLOC' \
+ '-DDISABLE_NACL' \
+ '-DCHROMIUM_BUILD' \
+ '-DUSE_LIBJPEG_TURBO=1' \
+ '-DUSE_PROPRIETARY_CODECS' \
+ '-DENABLE_PEPPER_THREADING' \
+ '-DENABLE_GPU=1' \
+ '-DUSE_OPENSSL=1' \
+ '-DENABLE_EGLIMAGE=1' \
+ '-DUSE_SKIA=1' \
+ '-DSK_BUILD_FOR_ANDROID_NDK' \
+ '-D__ARM_ARCH__=7' \
+ '-DANDROID' \
+ '-D__GNU_SOURCE=1' \
+ '-DUSE_STLPORT=1' \
+ '-D_STLP_USE_PTR_SPECIALIZATIONS=1' \
+ '-DCHROME_SYMBOLS_ID=""' \
+ '-DANDROID_UPSTREAM_BRINGUP=1' \
+ '-DDYNAMIC_ANNOTATIONS_ENABLED=1' \
+ '-DWTF_USE_DYNAMIC_ANNOTATIONS=1' \
+ '-D_DEBUG'
+
+LOCAL_CFLAGS := $(MY_CFLAGS_C) $(MY_CFLAGS) $(MY_DEFS)
+
+# Include paths placed before CFLAGS/CPPFLAGS
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/skia/config \
+ $(LOCAL_PATH)/third_party/skia/include/config \
+ $(LOCAL_PATH)/third_party/skia/include/core \
+ $(LOCAL_PATH)/third_party/skia/include/effects \
+ $(LOCAL_PATH)/third_party/skia/include/images \
+ $(LOCAL_PATH)/third_party/skia/include/utils \
+ $(LOCAL_PATH)/third_party/skia/src/core \
+ $(GYP_ABS_ANDROID_TOP_DIR)/frameworks/wilhelm/include \
+ $(GYP_ABS_ANDROID_TOP_DIR)/bionic \
+ $(GYP_ABS_ANDROID_TOP_DIR)/external/stlport/stlport
+
+LOCAL_C_INCLUDES := $(GYP_COPIED_SOURCE_ORIGIN_DIRS) $(LOCAL_C_INCLUDES)
+
+# Flags passed to only C++ (and not C) files.
+LOCAL_CPPFLAGS := \
+ -fno-rtti \
+ -fno-threadsafe-statics \
+ -fvisibility-inlines-hidden \
+ -Wno-deprecated \
+ -Wno-abi \
+ -Wno-error=c++0x-compat
+
+### Rules for final target.
+
+LOCAL_LDFLAGS := \
+ -Wl,-z,noexecstack \
+ -fPIC \
+ -Wl,-z,relro \
+ -Wl,-z,now \
+ -fuse-ld=gold \
+ -nostdlib \
+ -Wl,--no-undefined \
+ -Wl,--exclude-libs=ALL \
+ -Wl,--icf=safe \
+ -Wl,-O1 \
+ -Wl,--as-needed \
+ -Wl,--gc-sections
+
+
+LOCAL_STATIC_LIBRARIES :=
+
+# Enable grouping to fix circular references
+LOCAL_GROUP_STATIC_LIBRARIES := true
+
+LOCAL_SHARED_LIBRARIES := \
+ libstlport \
+ libdl
+
+# Add target alias to "gyp_all_modules" target.
+.PHONY: gyp_all_modules
+gyp_all_modules: skia_skia_opts_gyp
+
+# Alias gyp target name.
+.PHONY: skia_opts
+skia_opts: skia_skia_opts_gyp
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/skia/skia_test_expectations.txt b/skia/skia_test_expectations.txt
new file mode 100644
index 0000000000..d72efc6db4
--- /dev/null
+++ b/skia/skia_test_expectations.txt
@@ -0,0 +1,72 @@
+# 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
+
+# bungeman@ will rebaseline this test
+crbug.com/155867 platform/chromium-linux/fast/text/chromium-linux-fontconfig-renderstyle.html [ Failure ImageOnlyFailure ]
+
+# To be rebaselined after skia.gyp undefines SK_DISABLE_EXTRACTSUBSET_OPAQUE_FIX
+#
+crbug.com/156097 fast/backgrounds/repeat/negative-offset-repeat-transformed.html [ ImageOnlyFailure ]
+crbug.com/156097 fast/borders/border-image-rotate-transform.html [ ImageOnlyFailure ]
+crbug.com/156097 svg/custom/clip-mask-negative-scale.svg [ ImageOnlyFailure ]
+crbug.com/156097 svg/custom/focus-ring.svg [ ImageOnlyFailure ]
+crbug.com/156097 svg/transforms/animated-path-inside-transformed-html.xhtml [ ImageOnlyFailure ]
+crbug.com/156097 fast/forms/select-style.html [ ImageOnlyFailure ]
+
+# To be rebaselined by reed@. http://code.google.com/p/skia/source/detail?r=6220
+# removed double drawing of clipped hairlines at edges.
+
+crbug.com/148638 fast/repaint/list-marker.html [ Failure ImageOnlyFailure ]
+crbug.com/148638 svg/hixie/perf/001.xml [ Failure ImageOnlyFailure ]
+crbug.com/148638 svg/hixie/perf/002.xml [ Failure ImageOnlyFailure ]
+crbug.com/148638 svg/transforms/text-with-pattern-inside-transformed-html.xhtml [ Failure ImageOnlyFailure ]
+crbug.com/148638 svg/zoom/page/zoom-img-preserveAspectRatio-support-1.html [ Failure ImageOnlyFailure ]
+
+# END OVERRIDES HERE (this line ensures that the file is newline-terminated)
diff --git a/skia/tile_patch.diff b/skia/tile_patch.diff
new file mode 100644
index 0000000000..78118416b4
--- /dev/null
+++ b/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];
+ }
+