diff options
author | brettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-19 18:52:38 +0000 |
---|---|---|
committer | brettw@google.com <brettw@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-19 18:52:38 +0000 |
commit | 6165b4070b7e3a2fd33efd0eb66043c86f61d8b2 (patch) | |
tree | d2f7f195c13274ca524d264dc34639e276805325 /webkit | |
parent | 941ac7fca4afc89cb28ffa760f5a4a4ca077912c (diff) | |
download | chromium_src-6165b4070b7e3a2fd33efd0eb66043c86f61d8b2.zip chromium_src-6165b4070b7e3a2fd33efd0eb66043c86f61d8b2.tar.gz chromium_src-6165b4070b7e3a2fd33efd0eb66043c86f61d8b2.tar.bz2 |
Move PlatformCanvas and PlatformDevice from base/gfx to webkit/port. I left header files in the original locations that include the ones in the new location so I don't have to change all the includes in Chrome at once. These will be removed later.
I kept the names, indenting, and the namespaces the same for now. I will also be cleaning this up in separate passes.
Review URL: http://codereview.chromium.org/11244
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@5690 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit')
32 files changed, 4640 insertions, 0 deletions
diff --git a/webkit/SConscript.port b/webkit/SConscript.port index 7e0d5ca..bb7fa95 100644 --- a/webkit/SConscript.port +++ b/webkit/SConscript.port @@ -131,6 +131,9 @@ if env['PLATFORM'] == 'posix': '$PORT_DIR/platform/chromium/ScrollbarThemeChromiumLinux.cpp', '$PORT_DIR/platform/graphics/chromium/GlyphPageTreeNodeLinux.cpp', '$PORT_DIR/platform/graphics/skia/GdkSkia.cc', + + '$PORT_DIR/platform/graphics/skia/public/PlatformCanvasLinux.cpp', + '$PORT_DIR/platform/graphics/skia/public/PlatformDeviceLinux.cpp', ]) if env['PLATFORM'] == 'darwin': @@ -145,6 +148,9 @@ if env['PLATFORM'] == 'darwin': '$PORT_DIR/platform/chromium/ScrollbarThemeChromium.cpp', '$PORT_DIR/platform/graphics/FontCustomPlatformData.cpp', '$PORT_DIR/platform/graphics/ImageSkia.cpp', + + '$PORT_DIR/platform/graphics/skia/public/PlatformCanvasMac.cpp', + '$PORT_DIR/platform/graphics/skia/public/PlatformDeviceMac.cpp', ] for remove in remove_files: input_files.remove(remove) @@ -166,6 +172,12 @@ if env['PLATFORM'] == 'win32': '$PORT_DIR/platform/graphics/ThemeHelperWin.cpp', '$PORT_DIR/platform/graphics/UniscribeHelper.cpp', '$PORT_DIR/platform/graphics/UniscribeHelperTextRun.cpp', + + '$PORT_DIR/platform/graphics/skia/public/PlatformCanvasWin.cpp', + '$PORT_DIR/platform/graphics/skia/public/PlatformDeviceWin.cpp', + '$PORT_DIR/platform/graphics/skia/public/VectorCanvas.cpp', + '$PORT_DIR/platform/graphics/skia/public/VectorDevice.cpp', + '$PORT_DIR/rendering/RenderThemeWin.cpp', ]) diff --git a/webkit/build/port/port.vcproj b/webkit/build/port/port.vcproj index d521dda..579d049 100644 --- a/webkit/build/port/port.vcproj +++ b/webkit/build/port/port.vcproj @@ -1066,6 +1066,66 @@ > </File> </Filter> + <Filter + Name="skia" + > + <Filter + Name="public" + > + <File + RelativePath="..\..\port\platform\graphics\skia\public\BitmapPlatformDevice.h" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\BitmapPlatformDeviceWin.cpp" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\BitmapPlatformDeviceWin.h" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\PlatformCanvas.h" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\PlatformCanvasWin.cpp" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\PlatformCanvasWin.h" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\PlatformDevice.h" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\PlatformDeviceWin.cpp" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\PlatformDeviceWin.h" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\VectorCanvas.cpp" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\VectorCanvas.h" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\VectorDevice.cpp" + > + </File> + <File + RelativePath="..\..\port\platform\graphics\skia\public\VectorDevice.h" + > + </File> + </Filter> + </Filter> </Filter> <Filter Name="image-decoders" diff --git a/webkit/port/platform/graphics/skia/public/BitmapPlatformDevice.h b/webkit/port/platform/graphics/skia/public/BitmapPlatformDevice.h new file mode 100644 index 0000000..6fd9e88 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/BitmapPlatformDevice.h @@ -0,0 +1,30 @@ +// 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. + +// Declare a platform-neutral name for this platform's bitmap device class +// that can be used by upper-level classes that just need to pass a reference +// around. + +#if defined(WIN32) +#include "BitmapPlatformDeviceWin.h" +namespace gfx { + +typedef BitmapPlatformDeviceWin BitmapPlatformDevice; + +} // namespace gfx +#elif defined(__APPLE__) +#include "BitmapPlatformDeviceMac.h" +namespace gfx { + +typedef BitmapPlatformDeviceMac BitmapPlatformDevice; + +} // namespace gfx +#elif defined(__linux__) +#include "BitmapPlatformDeviceLinux.h" +namespace gfx { + +typedef BitmapPlatformDeviceLinux BitmapPlatformDevice; + +} // namespace gfx +#endif
\ No newline at end of file diff --git a/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceLinux.cpp b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceLinux.cpp new file mode 100644 index 0000000..c80a273 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceLinux.cpp @@ -0,0 +1,60 @@ +// 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 "base/gfx/bitmap_platform_device_linux.h" + +#include <cairo/cairo.h> + +#include "base/logging.h" + +namespace gfx { + +// 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. +BitmapPlatformDeviceLinux* BitmapPlatformDeviceLinux::Create( + int width, int height, bool is_opaque) { + cairo_surface_t* surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + width, height); + + 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); + +#ifndef NDEBUG + if (is_opaque) { + bitmap.eraseARGB(255, 0, 255, 128); // bright bluish green + } +#endif + + // The device object will take ownership of the graphics context. + return new BitmapPlatformDeviceLinux(bitmap, 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. +BitmapPlatformDeviceLinux::BitmapPlatformDeviceLinux(const SkBitmap& bitmap, + cairo_surface_t* surface) + : PlatformDeviceLinux(bitmap), + surface_(surface) { +} + +BitmapPlatformDeviceLinux::BitmapPlatformDeviceLinux( + const BitmapPlatformDeviceLinux& other) + : PlatformDeviceLinux(const_cast<BitmapPlatformDeviceLinux&>( + other).accessBitmap(true)) { +} + +BitmapPlatformDeviceLinux::~BitmapPlatformDeviceLinux() { + if (surface_) { + cairo_surface_destroy(surface_); + surface_ = NULL; + } +} + +} // namespace gfx diff --git a/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceLinux.h b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceLinux.h new file mode 100644 index 0000000..840df24 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceLinux.h @@ -0,0 +1,91 @@ +// 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 BitmapPlatformDeviceLinux_h +#define BitmapPlatformDeviceLinux_h + +#include "PLatformDeviceLinux.h" +#include "base/ref_counted.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 gfx { + +// ----------------------------------------------------------------------------- +// 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 BitmapPlatformDeviceLinux : public PlatformDeviceLinux { + // A reference counted cairo surface + class BitmapPlatformDeviceLinuxData; + + public: + /// Static constructor. I don't understand this, it's just a copy of the mac + static BitmapPlatformDeviceLinux* Create(int width, int height, + bool is_opaque); + + // 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. + BitmapPlatformDeviceLinux(const SkBitmap& other, + BitmapPlatformDeviceLinuxData* data); + virtual ~BitmapPlatformDeviceLinux(); + BitmapPlatformDeviceLinux& operator=(const BitmapPlatformDeviceLinux& other); + + // A stub copy constructor. Needs to be properly implemented. + BitmapPlatformDeviceLinux(const BitmapPlatformDeviceLinux& other); + + // Bitmaps aren't vector graphics. + virtual bool IsVectorial() { return false; } + + cairo_surface_t* surface() const; + + private: + scoped_refptr<BitmapPlatformDeviceLinuxData> data_; +}; + +} // namespace gfx + +#endif // BitmapPlatformDeviceLinux_h diff --git a/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceMac.cpp b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceMac.cpp new file mode 100755 index 0000000..570b07e --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceMac.cpp @@ -0,0 +1,289 @@ +// 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 <time.h> + +#include "SkMatrix.h" +#include "SkRegion.h" +#include "SkUtils.h" + +#include "base/gfx/bitmap_platform_device_mac.h" +#include "base/gfx/skia_utils_mac.h" +#include "base/logging.h" + +namespace gfx { + +namespace { + +// Constrains position and size to fit within available_size. If |size| is -1, +// all the |available_size| is used. Returns false if the position is out of +// |available_size|. +bool Constrain(int available_size, int* position, int *size) { + if (*size < -2) + return false; + + if (*position < 0) { + if (*size != -1) + *size += *position; + *position = 0; + } + if (*size == 0 || *position >= available_size) + return false; + + if (*size > 0) { + int overflow = (*position + *size) - available_size; + if (overflow > 0) { + *size -= overflow; + } + } else { + // Fill up available size. + *size = available_size - *position; + } + return true; +} + +} // namespace + +class BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData + : public base::RefCounted<BitmapPlatformDeviceMacData> { + public: + explicit BitmapPlatformDeviceMacData(CGContextRef bitmap); + + // Create/destroy CoreGraphics context for our bitmap data. + CGContextRef GetBitmapContext() { + LoadConfig(); + return bitmap_context_; + } + + void ReleaseBitmapContext() { + DCHECK(bitmap_context_); + CGContextRelease(bitmap_context_); + bitmap_context_ = NULL; + } + + // 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 DC. Can be called even when + // |bitmap_context_| is NULL (will be a NOP). + void LoadConfig(); + + // Lazily-created graphics context used to draw into the bitmap. + CGContextRef bitmap_context_; + + // True when there is a transform or clip that has not been set to the + // CGContext. The CGContext 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 CGContext: we need to keep track of this + // separately so it can be updated even if the CGContext isn't created yet. + SkMatrix transform_; + + // The current clipping + SkRegion clip_region_; + + private: + friend class base::RefCounted<BitmapPlatformDeviceMacData>; + ~BitmapPlatformDeviceMacData() { + if (bitmap_context_) + CGContextRelease(bitmap_context_); + } + + DISALLOW_COPY_AND_ASSIGN(BitmapPlatformDeviceMacData); +}; + +BitmapPlatformDeviceMac::\ + BitmapPlatformDeviceMacData::BitmapPlatformDeviceMacData( + CGContextRef bitmap) + : bitmap_context_(bitmap), + config_dirty_(true) { // Want to load the config next time. + DCHECK(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); + transform_.reset(); + CGContextRetain(bitmap_context_); +} + +void BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData::SetMatrixClip( + const SkMatrix& transform, + const SkRegion& region) { + transform_ = transform; + clip_region_ = region; + config_dirty_ = true; +} + +void BitmapPlatformDeviceMac::BitmapPlatformDeviceMacData::LoadConfig() { + if (!config_dirty_ || !bitmap_context_) + return; // Nothing to do. + config_dirty_ = false; + + // Transform. + SkMatrix t(transform_); + LoadTransformToCGContext(bitmap_context_, t); + t.setTranslateX(-t.getTranslateX()); + t.setTranslateY(-t.getTranslateY()); + LoadClippingRegionToCGContext(bitmap_context_, clip_region_, t); +} + + +// 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. +BitmapPlatformDeviceMac* BitmapPlatformDeviceMac::Create(CGContextRef context, + int width, + int height, + bool is_opaque) { + void* data = malloc(height * width * 4); + if (!data) return NULL; + + SkBitmap bitmap; + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap.setPixels(data); + + // Note: The Windows implementation clears the Bitmap later on. + // This bears mentioning since removal of this line makes the + // unit tests only fail periodically (or when MallocPreScribble is set). + bitmap.eraseARGB(0, 0, 0, 0); + + bitmap.setIsOpaque(is_opaque); + + if (is_opaque) { +#ifndef NDEBUG + // 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 + } + + CGColorSpaceRef color_space = + CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB); + // allocate a bitmap context with 4 components per pixel (RGBA): + CGContextRef bitmap_context = + CGBitmapContextCreate(data, width, height, 8, width*4, + color_space, kCGImageAlphaPremultipliedLast); + + // Change the coordinate system to match WebCore's + CGContextTranslateCTM(bitmap_context, 0, height); + CGContextScaleCTM(bitmap_context, 1.0, -1.0); + CGColorSpaceRelease(color_space); + + // The device object will take ownership of the graphics context. + return new BitmapPlatformDeviceMac( + new BitmapPlatformDeviceMacData(bitmap_context), bitmap); +} + +// 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. +BitmapPlatformDeviceMac::BitmapPlatformDeviceMac( + BitmapPlatformDeviceMacData* data, const SkBitmap& bitmap) + : PlatformDeviceMac(bitmap), + data_(data) { +} + +// The copy constructor just adds another reference to the underlying data. +// We use a const cast since the default Skia definitions don't define the +// proper constedness that we expect (accessBitmap should really be const). +BitmapPlatformDeviceMac::BitmapPlatformDeviceMac( + const BitmapPlatformDeviceMac& other) + : PlatformDeviceMac( + const_cast<BitmapPlatformDeviceMac&>(other).accessBitmap(true)), + data_(other.data_) { +} + +BitmapPlatformDeviceMac::~BitmapPlatformDeviceMac() { +} + +BitmapPlatformDeviceMac& BitmapPlatformDeviceMac::operator=( + const BitmapPlatformDeviceMac& other) { + data_ = other.data_; + return *this; +} + +CGContextRef BitmapPlatformDeviceMac::GetBitmapContext() { + return data_->GetBitmapContext(); +} + +void BitmapPlatformDeviceMac::setMatrixClip(const SkMatrix& transform, + const SkRegion& region) { + data_->SetMatrixClip(transform, region); +} + +void BitmapPlatformDeviceMac::DrawToContext(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; + if (src_rect) { + bounds = *src_rect; + bounds.origin.x = x; + bounds.origin.y = y; + CGImageRef sub_image = CGImageCreateWithImageInRect(image, *src_rect); + CGContextDrawImage(context, bounds, sub_image); + CGImageRelease(sub_image); + } else { + bounds.origin.x = 0; + bounds.origin.y = 0; + bounds.size.width = width(); + bounds.size.height = height(); + CGContextDrawImage(context, bounds, image); + } + CGImageRelease(image); + + if (created_dc) + data_->ReleaseBitmapContext(); +} + +// Returns the color value at the specified location. +SkColor BitmapPlatformDeviceMac::getColorAt(int x, int y) { + const SkBitmap& bitmap = accessBitmap(true); + SkAutoLockPixels lock(bitmap); + uint32_t* data = bitmap.getAddr32(0, 0); + return static_cast<SkColor>(data[x + y * width()]); +} + +void BitmapPlatformDeviceMac::onAccessBitmap(SkBitmap*) { + // Not needed in CoreGraphics +} + +void BitmapPlatformDeviceMac::processPixels(int x, int y, + int width, int height, + adjustAlpha adjustor) { + const SkBitmap& bitmap = accessBitmap(true); + SkMatrix& matrix = data_->transform_; + int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x; + int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y; + + SkAutoLockPixels lock(bitmap); + if (Constrain(bitmap.width(), &bitmap_start_x, &width) && + Constrain(bitmap.height(), &bitmap_start_y, &height)) { + uint32_t* data = bitmap.getAddr32(0, 0); + size_t row_words = bitmap.rowBytes() / 4; + for (int i = 0; i < height; i++) { + size_t offset = (i + bitmap_start_y) * row_words + bitmap_start_x; + for (int j = 0; j < width; j++) { + adjustor(data + offset + j); + } + } + } +} + +} // namespace gfx + diff --git a/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceMac.h b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceMac.h new file mode 100755 index 0000000..04383d7 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceMac.h @@ -0,0 +1,95 @@ +// 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 BitmapPlatformDeviceMac_h +#define BitmapPlatformDeviceMac_h + +#include "base/gfx/platform_device_mac.h" +#include "base/ref_counted.h" + +namespace gfx { + +// 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. BitmapPlatformDeviceMac 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 BitmapPlatformDeviceMac : public PlatformDeviceMac { + public: + // Factory function. The screen DC is used to create the bitmap, and will not + // be stored beyond this 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. + static BitmapPlatformDeviceMac* Create(CGContextRef context, + int width, + int height, + bool is_opaque); + + // Copy constructor. When copied, devices duplicate their internal data, so + // stay linked. This is because their implementation is very heavyweight + // (lots of memory and CoreGraphics state). If a device has been copied, both + // clip rects and other state will stay in sync. + // + // This means it will NOT work to duplicate a device and assign it to a + // canvas, because the two canvases will each set their own clip rects, and + // the resulting CoreGraphics drawing state will be unpredictable. + // + // Copy constucting and "=" is designed for saving the device or passing it + // around to another routine willing to deal with the bitmap data directly. + BitmapPlatformDeviceMac(const BitmapPlatformDeviceMac& other); + virtual ~BitmapPlatformDeviceMac(); + + // See warning for copy constructor above. + BitmapPlatformDeviceMac& operator=(const BitmapPlatformDeviceMac& other); + + virtual CGContextRef GetBitmapContext(); + virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region); + + virtual void DrawToContext(CGContextRef context, int x, int y, + const CGRect* src_rect); + virtual bool IsVectorial() { return false; } + virtual void fixupAlphaBeforeCompositing() { }; + + // Returns the color value at the specified location. This does not + // consider any transforms that may be set on the device. + SkColor getColorAt(int x, int y); + + 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 BitmapPlatformDeviceMacData; + + BitmapPlatformDeviceMac(BitmapPlatformDeviceMacData* 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 void onAccessBitmap(SkBitmap*); + + // Data associated with this device, guaranteed non-null. + scoped_refptr<BitmapPlatformDeviceMacData> data_; + + virtual void processPixels(int x, int y, + int width, int height, + adjustAlpha adjustor); +}; + +} // namespace gfx + +#endif // BitmapPlatformDeviceMac_h + diff --git a/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceWin.cpp b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceWin.cpp new file mode 100644 index 0000000..ceed2c8 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceWin.cpp @@ -0,0 +1,446 @@ +// 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 "BitmapPlatformDeviceWin.h" + +#include "base/gfx/gdi_util.h" +#include "base/logging.h" +#include "SkMatrix.h" +#include "SkRegion.h" +#include "SkUtils.h" + +namespace gfx { + +// When Windows draws text, is sets the fourth byte (which Skia uses for alpha) +// to zero. This means that if we try compositing with text that Windows has +// drawn, we get invalid color values (if the alpha is 0, the other channels +// should be 0 since Skia uses premultiplied colors) and strange results. +// +// HTML rendering only requires one bit of transparency. When you ask for a +// semitransparent div, the div itself is drawn in another layer as completely +// opaque, and then composited onto the lower layer with a transfer function. +// The only place an alpha channel is needed is to track what has been drawn +// and what has not been drawn. +// +// Therefore, when we allocate a new device, we fill it with this special +// color. Because Skia uses premultiplied colors, any color where the alpha +// channel is smaller than any component is impossible, so we know that no +// legitimate drawing will produce this color. We use 1 as the alpha value +// because 0 is produced when Windows draws text (even though it should be +// opaque). +// +// When a layer is done and we want to render it to a lower layer, we use +// fixupAlphaBeforeCompositing. This replaces all 0 alpha channels with +// opaque (to fix the text problem), and replaces this magic color value +// with transparency. The result is something that can be correctly +// composited. However, once this has been done, no more can be drawn to +// the layer because fixing the alphas *again* will result in incorrect +// values. +static const uint32_t kMagicTransparencyColor = 0x01FFFEFD; + +namespace { + +// Constrains position and size to fit within available_size. If |size| is -1, +// all the available_size is used. Returns false if the position is out of +// available_size. +bool Constrain(int available_size, int* position, int *size) { + if (*size < -2) + return false; + + if (*position < 0) { + if (*size != -1) + *size += *position; + *position = 0; + } + if (*size == 0 || *position >= available_size) + return false; + + if (*size > 0) { + int overflow = (*position + *size) - available_size; + if (overflow > 0) { + *size -= overflow; + } + } else { + // Fill up available size. + *size = available_size - *position; + } + return true; +} + +// If the pixel value is 0, it gets set to kMagicTransparencyColor. +void PrepareAlphaForGDI(uint32_t* pixel) { + if (*pixel == 0) { + *pixel = kMagicTransparencyColor; + } +} + +// If the pixel value is kMagicTransparencyColor, it gets set to 0. Otherwise +// if the alpha is 0, the alpha is set to 255. +void PostProcessAlphaForGDI(uint32_t* pixel) { + if (*pixel == kMagicTransparencyColor) { + *pixel = 0; + } else if ((*pixel & 0xFF000000) == 0) { + *pixel |= 0xFF000000; + } +} + +// Sets the opacity of the specified value to 0xFF. +void MakeOpaqueAlphaAdjuster(uint32_t* pixel) { + *pixel |= 0xFF000000; +} + +// See the declaration of kMagicTransparencyColor at the top of the file. +void FixupAlphaBeforeCompositing(uint32_t* pixel) { + if (*pixel == kMagicTransparencyColor) + *pixel = 0; + else + *pixel |= 0xFF000000; +} + +} // namespace + +class BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData + : public base::RefCounted<BitmapPlatformDeviceWinData> { + public: + explicit BitmapPlatformDeviceWinData(HBITMAP hbitmap); + + // Create/destroy hdc_, which is the memory DC for our bitmap data. + HDC GetBitmapDC(); + void ReleaseBitmapDC(); + bool IsBitmapDCCreated() const; + + // Sets the transform and clip operations. This will not update the DC, + // 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); + + const SkMatrix& transform() const { + return transform_; + } + + protected: + // Loads the current transform and clip into the DC. Can be called even when + // the DC is NULL (will be a NOP). + void LoadConfig(); + + // Windows bitmap corresponding to our surface. + HBITMAP hbitmap_; + + // Lazily-created DC used to draw into the bitmap, see getBitmapDC. + HDC hdc_; + + // True when there is a transform or clip that has not been set to the DC. + // The DC 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 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_; + + private: + friend class base::RefCounted<BitmapPlatformDeviceWinData>; + ~BitmapPlatformDeviceWinData(); + + DISALLOW_EVIL_CONSTRUCTORS(BitmapPlatformDeviceWinData); +}; + +BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::BitmapPlatformDeviceWinData( + HBITMAP hbitmap) + : hbitmap_(hbitmap), + hdc_(NULL), + config_dirty_(true) { // Want to load the config next time. + // Initialize the clip region to the entire bitmap. + BITMAP bitmap_data; + if (GetObject(hbitmap_, sizeof(BITMAP), &bitmap_data)) { + SkIRect rect; + rect.set(0, 0, bitmap_data.bmWidth, bitmap_data.bmHeight); + clip_region_ = SkRegion(rect); + } + + transform_.reset(); +} + +BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::~BitmapPlatformDeviceWinData() { + if (hdc_) + ReleaseBitmapDC(); + + // this will free the bitmap data as well as the bitmap handle + DeleteObject(hbitmap_); +} + +HDC BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::GetBitmapDC() { + if (!hdc_) { + hdc_ = CreateCompatibleDC(NULL); + InitializeDC(hdc_); + HGDIOBJ old_bitmap = SelectObject(hdc_, 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); + } + + LoadConfig(); + return hdc_; +} + +void BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::ReleaseBitmapDC() { + DCHECK(hdc_); + DeleteDC(hdc_); + hdc_ = NULL; +} + +bool BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::IsBitmapDCCreated() + const { + return hdc_ != NULL; +} + + +void BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::SetMatrixClip( + const SkMatrix& transform, + const SkRegion& region) { + transform_ = transform; + clip_region_ = region; + config_dirty_ = true; +} + +void BitmapPlatformDeviceWin::BitmapPlatformDeviceWinData::LoadConfig() { + if (!config_dirty_ || !hdc_) + return; // Nothing to do. + config_dirty_ = false; + + // Transform. + SkMatrix t(transform_); + LoadTransformToDC(hdc_, t); + // We don't use transform_ for the clipping region since the translation is + // already applied to offset_x_ and offset_y_. + t.reset(); + LoadClippingRegionToDC(hdc_, clip_region_, t); +} + +// 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. +BitmapPlatformDeviceWin* BitmapPlatformDeviceWin::create(HDC screen_dc, + int width, + int height, + bool is_opaque, + HANDLE shared_section) { + SkBitmap bitmap; + + // 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}; + CreateBitmapHeader(width, height, &hdr); + + void* data = NULL; + HBITMAP hbitmap = CreateDIBSection(screen_dc, + reinterpret_cast<BITMAPINFO*>(&hdr), 0, + &data, + shared_section, 0); + + // If we run out of GDI objects or some other error occurs, we won't get a + // bitmap here. This will cause us to crash later because the data pointer is + // NULL. To make sure that we can assign blame for those crashes to this code, + // we deliberately crash here, even in release mode. + if (!hbitmap) { + DWORD error = GetLastError(); + LOG(ERROR) << "CreateDIBSection Failed. Error: " << error << "\n"; + return NULL; + } + + bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap.setPixels(data); + bitmap.setIsOpaque(is_opaque); + + if (is_opaque) { +#ifndef NDEBUG + // 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 + } else { + // A transparent layer is requested: fill with our magic "transparent" + // color, see the declaration of kMagicTransparencyColor above + sk_memset32(static_cast<uint32_t*>(data), kMagicTransparencyColor, + width * height); + } + + // The device object will take ownership of the HBITMAP. + return new BitmapPlatformDeviceWin(new BitmapPlatformDeviceWinData(hbitmap), + bitmap); +} + +// 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. +BitmapPlatformDeviceWin::BitmapPlatformDeviceWin( + BitmapPlatformDeviceWinData* data, + const SkBitmap& bitmap) : PlatformDeviceWin(bitmap), data_(data) { +} + +// The copy constructor just adds another reference to the underlying data. +// We use a const cast since the default Skia definitions don't define the +// proper constedness that we expect (accessBitmap should really be const). +BitmapPlatformDeviceWin::BitmapPlatformDeviceWin( + const BitmapPlatformDeviceWin& other) + : PlatformDeviceWin( + const_cast<BitmapPlatformDeviceWin&>(other).accessBitmap(true)), + data_(other.data_) { +} + +BitmapPlatformDeviceWin::~BitmapPlatformDeviceWin() { +} + +BitmapPlatformDeviceWin& BitmapPlatformDeviceWin::operator=( + const BitmapPlatformDeviceWin& other) { + data_ = other.data_; + return *this; +} + +HDC BitmapPlatformDeviceWin::getBitmapDC() { + return data_->GetBitmapDC(); +} + +void BitmapPlatformDeviceWin::setMatrixClip(const SkMatrix& transform, + const SkRegion& region) { + data_->SetMatrixClip(transform, region); +} + +void BitmapPlatformDeviceWin::drawToHDC(HDC dc, int x, int y, + const RECT* src_rect) { + bool created_dc = !data_->IsBitmapDCCreated(); + HDC source_dc = getBitmapDC(); + + 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 { + DCHECK(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()); + + if (created_dc) + data_->ReleaseBitmapDC(); +} + +void BitmapPlatformDeviceWin::prepareForGDI(int x, int y, int width, + int height) { + processPixels<PrepareAlphaForGDI>(x, y, width, height); +} + +void BitmapPlatformDeviceWin::postProcessGDI(int x, int y, int width, + int height) { + processPixels<PostProcessAlphaForGDI>(x, y, width, height); +} + +void BitmapPlatformDeviceWin::makeOpaque(int x, int y, int width, int height) { + processPixels<MakeOpaqueAlphaAdjuster>(x, y, width, height); +} + +void BitmapPlatformDeviceWin::fixupAlphaBeforeCompositing() { + const SkBitmap& bitmap = accessBitmap(true); + SkAutoLockPixels lock(bitmap); + uint32_t* data = bitmap.getAddr32(0, 0); + + size_t words = bitmap.rowBytes() / sizeof(uint32_t) * bitmap.height(); + for (size_t i = 0; i < words; i++) { + if (data[i] == kMagicTransparencyColor) + data[i] = 0; + else + data[i] |= 0xFF000000; + } +} + +// Returns the color value at the specified location. +SkColor BitmapPlatformDeviceWin::getColorAt(int x, int y) { + const SkBitmap& bitmap = accessBitmap(false); + SkAutoLockPixels lock(bitmap); + uint32_t* data = bitmap.getAddr32(0, 0); + return static_cast<SkColor>(data[x + y * width()]); +} + +void BitmapPlatformDeviceWin::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(); +} + +template<BitmapPlatformDeviceWin::adjustAlpha adjustor> +void BitmapPlatformDeviceWin::processPixels(int x, + int y, + int width, + int height) { + const SkBitmap& bitmap = accessBitmap(true); + DCHECK_EQ(bitmap.config(), SkBitmap::kARGB_8888_Config); + const SkMatrix& matrix = data_->transform(); + int bitmap_start_x = SkScalarRound(matrix.getTranslateX()) + x; + int bitmap_start_y = SkScalarRound(matrix.getTranslateY()) + y; + + if (Constrain(bitmap.width(), &bitmap_start_x, &width) && + Constrain(bitmap.height(), &bitmap_start_y, &height)) { + SkAutoLockPixels lock(bitmap); + DCHECK_EQ(bitmap.rowBytes() % sizeof(uint32_t), 0u); + size_t row_words = bitmap.rowBytes() / sizeof(uint32_t); + // Set data to the first pixel to be modified. + uint32_t* data = bitmap.getAddr32(0, 0) + (bitmap_start_y * row_words) + + bitmap_start_x; + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + adjustor(data + j); + } + data += row_words; + } + } +} + +} // namespace gfx + diff --git a/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceWin.h b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceWin.h new file mode 100644 index 0000000..24008d4 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/BitmapPlatformDeviceWin.h @@ -0,0 +1,111 @@ +// 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 BitmapPlatformDeviceWin_h +#define BitmapPlatformDeviceWin_h + +#include "base/gfx/platform_device_win.h" +#include "base/ref_counted.h" + +namespace gfx { + +// 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. BitmapPlatformDeviceWin 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 BitmapPlatformDeviceWin : public PlatformDeviceWin { + public: + // Factory function. The screen DC is used to create the bitmap, and will not + // be stored beyond this 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. + static BitmapPlatformDeviceWin* create(HDC screen_dc, + int width, + int height, + bool is_opaque, + HANDLE shared_section); + + // Copy constructor. When copied, devices duplicate their internal data, so + // stay linked. This is because their implementation is very heavyweight + // (lots of memory and some GDI objects). If a device has been copied, both + // clip rects and other state will stay in sync. + // + // This means it will NOT work to duplicate a device and assign it to a + // canvas, because the two canvases will each set their own clip rects, and + // the resulting GDI clip rect will be random. + // + // Copy constucting and "=" is designed for saving the device or passing it + // around to another routine willing to deal with the bitmap data directly. + BitmapPlatformDeviceWin(const BitmapPlatformDeviceWin& other); + virtual ~BitmapPlatformDeviceWin(); + + // See warning for copy constructor above. + BitmapPlatformDeviceWin& operator=(const BitmapPlatformDeviceWin& other); + + // Retrieves the bitmap DC, which is the memory DC for our bitmap data. The + // bitmap DC is lazy created. + virtual HDC getBitmapDC(); + virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region); + + virtual void drawToHDC(HDC dc, int x, int y, const RECT* src_rect); + virtual void prepareForGDI(int x, int y, int width, int height); + virtual void postProcessGDI(int x, int y, int width, int height); + virtual void makeOpaque(int x, int y, int width, int height); + virtual void fixupAlphaBeforeCompositing(); + virtual bool IsVectorial() { return false; } + + // Returns the color value at the specified location. This does not + // consider any transforms that may be set on the device. + SkColor getColorAt(int x, int y); + + 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 void onAccessBitmap(SkBitmap* bitmap); + + private: + // Function pointer used by the processPixels method for setting the alpha + // value of a particular pixel. + typedef void (*adjustAlpha)(uint32_t* pixel); + + // 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 BitmapPlatformDeviceWinData; + + // Private constructor. + BitmapPlatformDeviceWin(BitmapPlatformDeviceWinData* data, + const SkBitmap& bitmap); + + // Loops through each of the pixels in the specified range, invoking + // adjustor for the alpha value of each pixel. If |width| or |height| are -1, + // the available width/height is used. + template<adjustAlpha adjustor> + void processPixels(int x, + int y, + int width, + int height); + + // Data associated with this device, guaranteed non-null. + scoped_refptr<BitmapPlatformDeviceWinData> data_; +}; + +} // namespace gfx + +#endif // BASE_GFX_BITMAP_PLATFORM_DEVICE_WIN_H_ + diff --git a/webkit/port/platform/graphics/skia/public/PlatformCanvas.h b/webkit/port/platform/graphics/skia/public/PlatformCanvas.h new file mode 100644 index 0000000..9fdfd68 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformCanvas.h @@ -0,0 +1,30 @@ +// 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. + +// Declare a platform-neutral name for this platform's canvas class +// that can be used by upper-level classes that just need to pass a reference +// around. + +#if defined(WIN32) +#include "PlatformCanvasWin.h" +namespace gfx { + +typedef PlatformCanvasWin PlatformCanvas; + +} // namespace gfx +#elif defined(__APPLE__) +#include "PlatformCanvasMac.h" +namespace gfx { + +typedef PlatformCanvasMac PlatformCanvas; + +} // namespace gfx +#elif defined(__linux__) +#include "PlatformCanvasLinux.h" +namespace gfx { + +typedef PlatformCanvasLinux PlatformCanvas; + +} // namespace gfx +#endif diff --git a/webkit/port/platform/graphics/skia/public/PlatformCanvasLinux.cpp b/webkit/port/platform/graphics/skia/public/PlatformCanvasLinux.cpp new file mode 100644 index 0000000..5ebeb27 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformCanvasLinux.cpp @@ -0,0 +1,52 @@ +// 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 "base/gfx/platform_canvas_linux.h" + +#include "base/gfx/platform_device_linux.h" +#include "base/gfx/bitmap_platform_device_linux.h" +#include "base/logging.h" + +namespace gfx { + +PlatformCanvasLinux::PlatformCanvasLinux() : SkCanvas() { +} + +PlatformCanvasLinux::PlatformCanvasLinux(int width, int height, bool is_opaque) + : SkCanvas() { + if (!initialize(width, height, is_opaque)) + CHECK(false); +} + +PlatformCanvasLinux::~PlatformCanvasLinux() { +} + +bool PlatformCanvasLinux::initialize(int width, int height, bool is_opaque) { + SkDevice* device = createPlatformDevice(width, height, is_opaque); + if (!device) + return false; + + setDevice(device); + device->unref(); // was created with refcount 1, and setDevice also refs + return true; +} + +PlatformDeviceLinux& PlatformCanvasLinux::getTopPlatformDevice() const { + // All of our devices should be our special PlatformDevice. + SkCanvas::LayerIter iter(const_cast<PlatformCanvasLinux*>(this), false); + return *static_cast<PlatformDeviceLinux*>(iter.device()); +} + +SkDevice* PlatformCanvasLinux::createDevice(SkBitmap::Config, int width, + int height, bool is_opaque) { + return createPlatformDevice(width, height, is_opaque); +} + +SkDevice* PlatformCanvasLinux::createPlatformDevice(int width, + int height, + bool is_opaque) { + return BitmapPlatformDeviceLinux::Create(width, height, is_opaque); +} + +} // namespace gfx diff --git a/webkit/port/platform/graphics/skia/public/PlatformCanvasLinux.h b/webkit/port/platform/graphics/skia/public/PlatformCanvasLinux.h new file mode 100644 index 0000000..d6f5d2b --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformCanvasLinux.h @@ -0,0 +1,52 @@ +// 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 PlatformCanvasLinux_h +#define PlatformCanvasLinux_h + +#include "PlatformCanvasLinux.h" + +namespace gfx { + +// This class is a specialization of the regular SkCanvas that is designed to +// work with a gfx::PlatformDevice to manage platform-specific drawing. It +// allows using both Skia operations and platform-specific operations. +class PlatformCanvasLinux : public SkCanvas { + public: + // Set is_opaque if you are going to erase the bitmap and not use + // tranparency: this will enable some optimizations. The shared_section + // parameter is passed to gfx::PlatformDevice::create. See it for details. + // + // If you use the version with no arguments, you MUST call initialize() + PlatformCanvasLinux(); + PlatformCanvasLinux(int width, int height, bool is_opaque); + virtual ~PlatformCanvasLinux(); + + // For two-part init, call if you use the no-argument constructor above + bool initialize(int width, int height, bool is_opaque); + + // Returns the platform device pointer of the topmost rect with a non-empty + // clip. Both the windows and mac versions have an equivalent of this method; + // a Linux version is added for compatibility. + PlatformDeviceLinux& getTopPlatformDevice() const; + + protected: + // Creates a device store for use by the canvas. We override this so that + // the device is always our own so we know that we can use GDI operations + // on it. Simply calls into createPlatformDevice(). + virtual SkDevice* createDevice(SkBitmap::Config, int width, int height, + bool is_opaque); + + // Creates a device store for use by the canvas. By default, it creates a + // BitmapPlatformDevice object. Can be overridden to change the object type. + virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque); + + // Disallow copy and assign. + PlatformCanvasLinux(const PlatformCanvasLinux&); + PlatformCanvasLinux& operator=(const PlatformCanvasLinux&); +}; + +} // namespace gfx + +#endif // PlatformCanvasLinux_h diff --git a/webkit/port/platform/graphics/skia/public/PlatformCanvasMac.cpp b/webkit/port/platform/graphics/skia/public/PlatformCanvasMac.cpp new file mode 100755 index 0000000..e78ade7 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformCanvasMac.cpp @@ -0,0 +1,80 @@ +// 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 "PlatformCanvasMac.h" + +#include "base/gfx/bitmap_platform_device_mac.h" +#include "base/logging.h" + +namespace gfx { + +PlatformCanvasMac::PlatformCanvasMac() : SkCanvas() { +} + +PlatformCanvasMac::PlatformCanvasMac(int width, int height, bool is_opaque) + : SkCanvas() { + initialize(width, height, is_opaque); +} + +PlatformCanvasMac::PlatformCanvasMac(int width, + int height, + bool is_opaque, + CGContextRef context) + : SkCanvas() { + initialize(width, height, is_opaque); +} + +PlatformCanvasMac::~PlatformCanvasMac() { +} + +bool PlatformCanvasMac::initialize(int width, + int height, + bool is_opaque) { + SkDevice* device = createPlatformDevice(width, height, is_opaque, NULL); + if (!device) + return false; + + setDevice(device); + device->unref(); // was created with refcount 1, and setDevice also refs + return true; +} + +CGContextRef PlatformCanvasMac::beginPlatformPaint() { + return getTopPlatformDevice().GetBitmapContext(); +} + +void PlatformCanvasMac::endPlatformPaint() { + // flushing will be done in onAccessBitmap +} + +PlatformDeviceMac& PlatformCanvasMac::getTopPlatformDevice() const { + // All of our devices should be our special PlatformDeviceMac. + SkCanvas::LayerIter iter(const_cast<PlatformCanvasMac*>(this), false); + return *static_cast<PlatformDeviceMac*>(iter.device()); +} + +SkDevice* PlatformCanvasMac::createDevice(SkBitmap::Config config, + int width, + int height, + bool is_opaque, bool isForLayer) { + DCHECK(config == SkBitmap::kARGB_8888_Config); + return createPlatformDevice(width, height, is_opaque, NULL); +} + +SkDevice* PlatformCanvasMac::createPlatformDevice(int width, + int height, + bool is_opaque, + CGContextRef context) { + SkDevice* device = BitmapPlatformDeviceMac::Create(context, width, height, + is_opaque); + return device; +} + +SkDevice* PlatformCanvasMac::setBitmapDevice(const SkBitmap&) { + NOTREACHED(); + return NULL; +} + +} // namespace gfx + diff --git a/webkit/port/platform/graphics/skia/public/PlatformCanvasMac.h b/webkit/port/platform/graphics/skia/public/PlatformCanvasMac.h new file mode 100755 index 0000000..8a39637 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformCanvasMac.h @@ -0,0 +1,88 @@ +// 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 PlatformCanvasMac_h +#define PlatformCanvasMac_h + +#include "PlatformDeviceMac.h" + +#include "SkCanvas.h" + +namespace gfx { + +// This class is a specialization of the regular SkCanvas that is designed to +// work with a gfx::PlatformDevice to manage platform-specific drawing. It +// allows using both Skia operations and platform-specific operations. +class PlatformCanvasMac : public SkCanvas { + public: + // Set is_opaque if you are going to erase the bitmap and not use + // tranparency: this will enable some optimizations. The shared_section + // parameter is passed to gfx::PlatformDevice::create. See it for details. + // + // If you use the version with no arguments, you MUST call initialize() + PlatformCanvasMac(); + PlatformCanvasMac(int width, int height, bool is_opaque); + PlatformCanvasMac(int width, int height, bool is_opaque, CGContextRef context); + virtual ~PlatformCanvasMac(); + + // For two-part init, call if you use the no-argument constructor above + bool initialize(int width, int height, bool is_opaque); + + // These calls should surround calls to platform drawing routines. The CG + // context returned by beginPlatformPaint is the one that can be used to + // draw into. + // Call endPlatformPaint when you are done and want to use Skia operations + // again; this will synchronize the bitmap. + virtual CGContextRef beginPlatformPaint(); + virtual void endPlatformPaint(); + + // Returns the platform device 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(). + PlatformDeviceMac& getTopPlatformDevice() const; + + // Allow callers to see the non-virtual function even though we have an + // override of a virtual one. + using SkCanvas::clipRect; + + protected: + // Creates a device store for use by the canvas. We override this so that + // the device is always our own so we know that we can use GDI operations + // on it. Simply calls into createPlatformDevice(). + virtual SkDevice* createDevice(SkBitmap::Config, int width, int height, + bool is_opaque, bool isForLayer); + + // Creates a device store for use by the canvas. By default, it creates a + // BitmapPlatformDevice object. Can be overridden to change the object type. + virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque, + CGContextRef context); + + private: + // Unimplemented. This is to try to prevent people from calling this function + // on SkCanvas. SkCanvas' version is not virtual, so we can't prevent this + // 100%, but hopefully this will make people notice and not use the function. + // Calling SkCanvas' version will create a new device which is not compatible + // with us and we will crash if somebody tries to draw into it with + // CoreGraphics. + SkDevice* setBitmapDevice(const SkBitmap& bitmap); + + // Disallow copy and assign. + PlatformCanvasMac(const PlatformCanvasMac&); + PlatformCanvasMac& operator=(const PlatformCanvasMac&); +}; + +} // namespace gfx + +#endif // PlatformCanvasMac_h + diff --git a/webkit/port/platform/graphics/skia/public/PlatformCanvasWin.cpp b/webkit/port/platform/graphics/skia/public/PlatformCanvasWin.cpp new file mode 100644 index 0000000..6c0fe08 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformCanvasWin.cpp @@ -0,0 +1,118 @@ +// 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 "PlatformCanvasWin.h" + +#include "BitmapPlatformDeviceWin.h" + +#include "base/logging.h" +#include "base/process_util.h" + +namespace gfx { + +// Crashes the process. This is called when a bitmap allocation fails, and this +// function tries to determine why it might have failed, and crash on different +// lines. This allows us to see in crash dumps the most likely reason for the +// failure. It takes the size of the bitmap we were trying to allocate as its +// arguments so we can check that as well. +void CrashForBitmapAllocationFailure(int w, int h) { + // The maximum number of GDI objects per process is 10K. If we're very close + // to that, it's probably the problem. + const int kLotsOfGDIObjs = 9990; + CHECK(GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS) < kLotsOfGDIObjs); + + // If the bitmap is ginormous, then we probably can't allocate it. + // We use 64M pixels = 256MB @ 4 bytes per pixel. + const int64 kGinormousBitmapPxl = 64000000; + CHECK(static_cast<int64>(w) * static_cast<int64>(h) < kGinormousBitmapPxl); + + // If we're using a crazy amount of virtual address space, then maybe there + // isn't enough for our bitmap. + const int64 kLotsOfMem = 1500000000; // 1.5GB. + scoped_ptr<base::ProcessMetrics> process_metrics( + base::ProcessMetrics::CreateProcessMetrics(GetCurrentProcess())); + CHECK(process_metrics->GetPagefileUsage() < kLotsOfMem); + + // Everything else. + CHECK(0); +} + + +PlatformCanvasWin::PlatformCanvasWin() : SkCanvas() { +} + +PlatformCanvasWin::PlatformCanvasWin(int width, int height, bool is_opaque) + : SkCanvas() { + bool initialized = initialize(width, height, is_opaque, NULL); + if (!initialized) + CrashForBitmapAllocationFailure(width, height); +} + +PlatformCanvasWin::PlatformCanvasWin(int width, + int height, + bool is_opaque, + HANDLE shared_section) + : SkCanvas() { + bool initialized = initialize(width, height, is_opaque, shared_section); + if (!initialized) + CrashForBitmapAllocationFailure(width, height); +} + +PlatformCanvasWin::~PlatformCanvasWin() { +} + +bool PlatformCanvasWin::initialize(int width, + int height, + bool is_opaque, + HANDLE shared_section) { + SkDevice* device = + createPlatformDevice(width, height, is_opaque, shared_section); + if (!device) + return false; + + setDevice(device); + device->unref(); // was created with refcount 1, and setDevice also refs + return true; +} + +HDC PlatformCanvasWin::beginPlatformPaint() { + return getTopPlatformDevice().getBitmapDC(); +} + +void PlatformCanvasWin::endPlatformPaint() { + // we don't clear the DC here since it will be likely to be used again + // flushing will be done in onAccessBitmap +} + +PlatformDeviceWin& PlatformCanvasWin::getTopPlatformDevice() const { + // All of our devices should be our special PlatformDevice. + SkCanvas::LayerIter iter(const_cast<PlatformCanvasWin*>(this), false); + return *static_cast<PlatformDeviceWin*>(iter.device()); +} + +SkDevice* PlatformCanvasWin::createDevice(SkBitmap::Config config, + int width, + int height, + bool is_opaque, bool isForLayer) { + DCHECK(config == SkBitmap::kARGB_8888_Config); + return createPlatformDevice(width, height, is_opaque, NULL); +} + +SkDevice* PlatformCanvasWin::createPlatformDevice(int width, + int height, + bool is_opaque, + HANDLE shared_section) { + HDC screen_dc = GetDC(NULL); + SkDevice* device = BitmapPlatformDeviceWin::create(screen_dc, width, height, + is_opaque, shared_section); + ReleaseDC(NULL, screen_dc); + return device; +} + +SkDevice* PlatformCanvasWin::setBitmapDevice(const SkBitmap&) { + NOTREACHED(); + return NULL; +} + +} // namespace gfx diff --git a/webkit/port/platform/graphics/skia/public/PlatformCanvasWin.h b/webkit/port/platform/graphics/skia/public/PlatformCanvasWin.h new file mode 100644 index 0000000..fa84693 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformCanvasWin.h @@ -0,0 +1,202 @@ +// 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 PlatformCanvasWin_h +#define PlatformCanvasWin_h + +#include <windows.h> + +#include "PlatformDeviceWin.h" +#include "base/basictypes.h" + +#include "SkCanvas.h" + +namespace gfx { + +// This class is a specialization of the regular SkCanvas that is designed to +// work with a gfx::PlatformDevice to manage platform-specific drawing. It +// allows using both Skia operations and platform-specific operations. +class PlatformCanvasWin : public SkCanvas { + public: + // Set is_opaque if you are going to erase the bitmap and not use + // transparency: this will enable some optimizations. The shared_section + // parameter is passed to gfx::PlatformDevice::create. See it for details. + // + // If you use the version with no arguments, you MUST call initialize() + PlatformCanvasWin(); + PlatformCanvasWin(int width, int height, bool is_opaque); + PlatformCanvasWin(int width, int height, bool is_opaque, + HANDLE shared_section); + virtual ~PlatformCanvasWin(); + + // 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); + + // These calls should surround calls to platform drawing routines, the DC + // returned by beginPlatformPaint is the DC that can be used to draw into. + // Call endPlatformPaint when you are done and want to use Skia operations + // again; this will synchronize the bitmap to Windows. + virtual HDC beginPlatformPaint(); + virtual void endPlatformPaint(); + + // Returns the platform device 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(). + PlatformDeviceWin& getTopPlatformDevice() const; + + protected: + // Creates a device store for use by the canvas. We override this so that + // the device is always our own so we know that we can use GDI operations + // on it. Simply calls into createPlatformDevice(). + virtual SkDevice* createDevice(SkBitmap::Config, int width, int height, + bool is_opaque, bool isForLayer); + + // Creates a device store for use by the canvas. By default, it creates a + // BitmapPlatformDeviceWin. Can be overridden to change the object type. + virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque, + HANDLE shared_section); + + private: + // Unimplemented. + virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap); + + // Disallow copy and assign. + PlatformCanvasWin(const PlatformCanvasWin&); + PlatformCanvasWin& operator=(const PlatformCanvasWin&); +}; + +// A class designed to help with WM_PAINT operations on Windows. It will +// do BeginPaint/EndPaint on init/destruction, and will create the bitmap and +// canvas with the correct size and transform for the dirty rect. The bitmap +// will be automatically painted to the screen on destruction. +// +// You MUST call isEmpty before painting to determine if anything needs +// painting. Sometimes the dirty rect can actually be empty, and this makes +// the bitmap functions we call unhappy. The caller should not paint in this +// case. +// +// Therefore, all you need to do is: +// case WM_PAINT: { +// gfx::PlatformCanvasWinPaint canvas(hwnd); +// if (!canvas.isEmpty()) { +// ... paint to the canvas ... +// } +// return 0; +// } +template <class T> +class CanvasPaintT : public T { + public: + CanvasPaintT(HWND hwnd) : hwnd_(hwnd), paint_dc_(NULL), for_paint_(true) { + memset(&ps_, 0, sizeof(ps_)); + initPaint(true); + } + + CanvasPaintT(HWND hwnd, bool opaque) : hwnd_(hwnd), paint_dc_(NULL), + for_paint_(true) { + memset(&ps_, 0, sizeof(ps_)); + initPaint(opaque); + } + + // Creates a CanvasPaintT for the specified region that paints to the + // specified dc. This does NOT do BeginPaint/EndPaint. + CanvasPaintT(HDC dc, bool opaque, int x, int y, int w, int h) + : hwnd_(NULL), + paint_dc_(dc), + for_paint_(false) { + memset(&ps_, 0, sizeof(ps_)); + ps_.rcPaint.left = x; + ps_.rcPaint.right = x + w; + ps_.rcPaint.top = y; + ps_.rcPaint.bottom = y + h; + init(opaque); + } + + + virtual ~CanvasPaintT() { + if (!isEmpty()) { + restoreToCount(1); + // Commit the drawing to the screen + getTopPlatformDevice().drawToHDC(paint_dc_, + ps_.rcPaint.left, ps_.rcPaint.top, + NULL); + } + if (for_paint_) + EndPaint(hwnd_, &ps_); + } + + // Returns true if the invalid region is empty. The caller should call this + // function to determine if anything needs painting. + bool isEmpty() const { + return ps_.rcPaint.right - ps_.rcPaint.left == 0 || + ps_.rcPaint.bottom - ps_.rcPaint.top == 0; + } + + // Use to access the Windows painting parameters, especially useful for + // getting the bounding rect for painting: paintstruct().rcPaint + const PAINTSTRUCT& paintStruct() const { + return ps_; + } + + // Returns the DC that will be painted to + HDC paintDC() const { + return paint_dc_; + } + + protected: + HWND hwnd_; + HDC paint_dc_; + PAINTSTRUCT ps_; + + private: + void initPaint(bool opaque) { + paint_dc_ = BeginPaint(hwnd_, &ps_); + + init(opaque); + } + + void init(bool opaque) { + // FIXME(brettw) for ClearType, we probably want to expand the bounds of + // painting by one pixel so that the boundaries will be correct (ClearType + // text can depend on the adjacent pixel). Then we would paint just the + // inset pixels to the screen. + const int width = ps_.rcPaint.right - ps_.rcPaint.left; + const int height = ps_.rcPaint.bottom - ps_.rcPaint.top; + if (!initialize(width, height, opaque, NULL)) { + // Cause a deliberate crash; + *(char*) 0 = 0; + } + + // This will bring the canvas into the screen coordinate system for the + // dirty rect + translate(SkIntToScalar(-ps_.rcPaint.left), + SkIntToScalar(-ps_.rcPaint.top)); + } + + // If true, this canvas was created for a BeginPaint. + const bool for_paint_; + + // Disallow copy and assign. + CanvasPaintT(const CanvasPaintT&); + CanvasPaintT& operator=(const CanvasPaintT&); +}; + +typedef CanvasPaintT<PlatformCanvasWin> PlatformCanvasWinPaint; + +} // namespace gfx + +#endif // PlatformCanvasWin_h + diff --git a/webkit/port/platform/graphics/skia/public/PlatformCanvas_unittest.cpp b/webkit/port/platform/graphics/skia/public/PlatformCanvas_unittest.cpp new file mode 100644 index 0000000..8e227ad --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformCanvas_unittest.cpp @@ -0,0 +1,296 @@ +// 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. + +// TODO(awalker): clean up the const/non-const reference handling in this test + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#else +#include <unistd.h> +#endif + +#include "base/gfx/platform_canvas.h" +#include "base/gfx/platform_device.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "SkColor.h" + +namespace gfx { + +namespace { + +// Return true if the canvas is filled to canvas_color, +// and contains a single rectangle filled to rect_color. +bool VerifyRect(const PlatformCanvas& canvas, + uint32_t canvas_color, uint32_t rect_color, + int x, int y, int w, int h) { + PlatformDevice& device = canvas.getTopPlatformDevice(); + const SkBitmap& bitmap = device.accessBitmap(false); + SkAutoLockPixels lock(bitmap); + + 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) != rect_color) + return false; + } else { + // Outside the square should be canvas_color + if (*bitmap.getAddr32(cur_x, cur_y) != canvas_color) + return false; + } + } + } + return true; +} + +// Checks whether there is a white canvas with a black square at the given +// location in pixels (not in the canvas coordinate system). +// TODO(ericroman): rename Square to Rect +bool VerifyBlackSquare(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) { + HDC dc = canvas.beginPlatformPaint(); + + 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))); + + canvas.endPlatformPaint(); +} +#elif defined(OS_MACOSX) +void DrawNativeRect(PlatformCanvas& canvas, int x, int y, int w, int h) { + CGContextRef context = canvas.beginPlatformPaint(); + + 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); + + canvas.endPlatformPaint(); +} +#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); + } + + ~LayerSaver() { + canvas_.getTopPlatformDevice().fixupAlphaBeforeCompositing(); + 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; + +} + +// 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(VerifyBlackSquare(canvas, kLayerX, kLayerY, kLayerW, kLayerH)); +} + +// 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); + canvas.getTopPlatformDevice().fixupAlphaBeforeCompositing(); + 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)); +} + +// 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); + } + EXPECT_TRUE(VerifyBlackSquare(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); + } + EXPECT_TRUE(VerifyBlackSquare(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); + canvas.restore(); + } + EXPECT_TRUE(VerifyBlackSquare(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); + } + canvas.restore(); + EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX, kInnerY, kInnerW, kInnerH)); +} + +// 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); + } + canvas.restore(); + EXPECT_TRUE(VerifyBlackSquare(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); + } + canvas.restore(); + EXPECT_TRUE(VerifyBlackSquare(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); + } + canvas.restore(); + EXPECT_TRUE(VerifyBlackSquare(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.translate(1, 1); + AddClip(canvas, kInnerX, kInnerY, kInnerW, kInnerH); + DrawNativeRect(canvas, 0, 0, 100, 100); + } + canvas.restore(); + EXPECT_TRUE(VerifyBlackSquare(canvas, kInnerX + 2, kInnerY + 2, + kInnerW, kInnerH)); +} + +} // namespace + diff --git a/webkit/port/platform/graphics/skia/public/PlatformDevice.h b/webkit/port/platform/graphics/skia/public/PlatformDevice.h new file mode 100644 index 0000000..3072e08 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformDevice.h @@ -0,0 +1,27 @@ +// 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. + +// Declare a platform-neutral name for this platform's device class +// that can be used by upper-level classes that just need to pass a reference +// around. + +#if defined(WIN32) +#include "PlatformDeviceWin.h" +#elif defined(__APPLE__) +#include "PlatformDeviceMac.h" +#elif defined(__linux__) +#include "PlatformDeviceLinux.h" +#endif + +namespace gfx { + +#if defined(WIN32) +typedef PlatformDeviceWin PlatformDevice; +#elif defined(__APPLE__) +typedef PlatformDeviceMac PlatformDevice; +#elif defined(__linux__) +typedef PlatformDeviceLinux PlatformDevice; +#endif + +} // namespace gfx diff --git a/webkit/port/platform/graphics/skia/public/PlatformDeviceLinux.cpp b/webkit/port/platform/graphics/skia/public/PlatformDeviceLinux.cpp new file mode 100644 index 0000000..50ffd51 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformDeviceLinux.cpp @@ -0,0 +1,13 @@ +// 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 "PlatformDeviceLinux.h" + +namespace gfx { + +PlatformDeviceLinux::PlatformDeviceLinux(const SkBitmap& bitmap) + : SkDevice(bitmap) { +} + +} // namespace gfx diff --git a/webkit/port/platform/graphics/skia/public/PlatformDeviceLinux.h b/webkit/port/platform/graphics/skia/public/PlatformDeviceLinux.h new file mode 100644 index 0000000..7c853a4 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformDeviceLinux.h @@ -0,0 +1,25 @@ +// 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 PlatformDeviceLinux_h +#define PlatformDeviceLinux_h + +#include "SkDevice.h" + +namespace gfx { + +// Blindly copying the mac hierarchy. +class PlatformDeviceLinux : public SkDevice { + public: + // Returns if the preferred rendering engine is vectorial or bitmap based. + virtual bool IsVectorial() = 0; + + protected: + // Forwards |bitmap| to SkDevice's constructor. + PlatformDeviceLinux(const SkBitmap& bitmap); +}; + +} // namespace gfx + +#endif // PlatformDeviceLinux_h diff --git a/webkit/port/platform/graphics/skia/public/PlatformDeviceMac.cpp b/webkit/port/platform/graphics/skia/public/PlatformDeviceMac.cpp new file mode 100755 index 0000000..6addc22 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformDeviceMac.cpp @@ -0,0 +1,161 @@ +// 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 "PlatformDeviceMac.h" + +#include "base/logging.h" +#include "base/gfx/skia_utils_mac.h" +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkUtils.h" + +namespace gfx { + +namespace { + +// Constrains position and size to fit within available_size. +bool constrain(int available_size, int* position, int *size) { + if (*position < 0) { + *size += *position; + *position = 0; + } + if (*size > 0 && *position < available_size) { + int overflow = (*position + *size) - available_size; + if (overflow > 0) { + *size -= overflow; + } + return true; + } + return false; +} + +// Sets the opacity of the specified value to 0xFF. +void makeOpaqueAlphaAdjuster(uint32_t* pixel) { + *pixel |= 0xFF000000; +} + +} // namespace + +PlatformDeviceMac::PlatformDeviceMac(const SkBitmap& bitmap) + : SkDevice(bitmap) { +} + +void PlatformDeviceMac::makeOpaque(int x, int y, int width, int height) { + processPixels(x, y, width, height, makeOpaqueAlphaAdjuster); +} + +// Set up the CGContextRef for peaceful coexistence with Skia +void PlatformDeviceMac::InitializeCGContext(CGContextRef context) { + // CG defaults to the same settings as Skia +} + +// static +void PlatformDeviceMac::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: { + NOTREACHED(); + break; + } + } + } + CGContextClosePath(context); +} + +// static +void PlatformDeviceMac::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. + DCHECK(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 = SkMatrixToCGAffineTransform(transformed_matrix); + + // Load final transform into context. + CGContextConcatCTM(context, cg_matrix); +} + +// static +void PlatformDeviceMac::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, SkRectToCGRect(rect)); + } else if (region.isRect()) { + // Do the transformation. + SkRect rect; + rect.set(region.getBounds()); + transformation.mapRect(&rect); + SkIRect irect; + rect.round(&irect); + CGContextClipToRect(context, 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. + NOTREACHED(); + // LoadPathToDC(context, path); + // hrgn = PathToRegion(context); + } +} + +} // namespace gfx + diff --git a/webkit/port/platform/graphics/skia/public/PlatformDeviceMac.h b/webkit/port/platform/graphics/skia/public/PlatformDeviceMac.h new file mode 100755 index 0000000..0e1dc61 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformDeviceMac.h @@ -0,0 +1,86 @@ +// 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 PlatformDeviceMac_h +#define PlatformDeviceMac_h + +#import <ApplicationServices/ApplicationServices.h> +#include "SkDevice.h" + +class SkMatrix; +class SkPath; +class SkRegion; + +namespace gfx { + +// 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. It also provides functionality to play well with CG 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. +class PlatformDeviceMac : public SkDevice { + public: + // 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; + + // Draws to the given graphics context. If the bitmap context doesn't exist, + // this will temporarily create it. However, if you have created the bitmap + // context, 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 DrawToContext(CGContextRef context, int x, int y, + const CGRect* src_rect) = 0; + + // Sets the opacity of each pixel in the specified region to be opaque. + void makeOpaque(int x, int y, int width, int height); + + // Returns if the preferred rendering engine is vectorial or bitmap based. + virtual bool IsVectorial() = 0; + + // On platforms where the native rendering API does not support rendering + // into bitmaps with a premultiplied alpha channel, this call is responsible + // for doing any fixup necessary. It is not used on the Mac, since + // CoreGraphics can handle premultiplied alpha just fine. + virtual void fixupAlphaBeforeCompositing() = 0; + + // Initializes the default settings and colors in a device context. + static void InitializeCGContext(CGContextRef context); + + // 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); + + // Loads a SkRegion into the CG context. + static void LoadClippingRegionToCGContext(CGContextRef context, + const SkRegion& region, + const SkMatrix& transformation); + + protected: + // Forwards |bitmap| to SkDevice's constructor. + PlatformDeviceMac(const SkBitmap& bitmap); + + // Loads the specified Skia transform into the device context + static void LoadTransformToCGContext(CGContextRef context, + const SkMatrix& matrix); + + // Function pointer used by the processPixels method for setting the alpha + // value of a particular pixel. + typedef void (*adjustAlpha)(uint32_t* pixel); + + // Loops through each of the pixels in the specified range, invoking + // adjustor for the alpha value of each pixel. + virtual void processPixels(int x, + int y, + int width, + int height, + adjustAlpha adjustor) = 0; +}; + +} // namespace gfx + +#endif // PlatformDeviceMac_h + diff --git a/webkit/port/platform/graphics/skia/public/PlatformDeviceWin.cpp b/webkit/port/platform/graphics/skia/public/PlatformDeviceWin.cpp new file mode 100644 index 0000000..65dcffc --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformDeviceWin.cpp @@ -0,0 +1,229 @@ +// 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 "PlatformDeviceWin.h" + +#include "base/logging.h" +#include "base/gfx/skia_utils.h" +#include "SkMatrix.h" +#include "SkPath.h" +#include "SkRegion.h" +#include "SkUtils.h" + +namespace gfx { + +PlatformDeviceWin::PlatformDeviceWin(const SkBitmap& bitmap) + : SkDevice(bitmap) { +} + +// static +void PlatformDeviceWin::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); + DCHECK_NE(res, 0); + + // Enables dithering. + res = SetStretchBltMode(context, HALFTONE); + DCHECK_NE(res, 0); + // As per SetStretchBltMode() documentation, SetBrushOrgEx() must be called + // right after. + res = SetBrushOrgEx(context, 0, 0, NULL); + DCHECK_NE(res, 0); + + // Sets up default orientation. + res = SetArcDirection(context, AD_CLOCKWISE); + DCHECK_NE(res, 0); + + // Sets up default colors. + res = SetBkColor(context, RGB(255, 255, 255)); + DCHECK_NE(res, CLR_INVALID); + res = SetTextColor(context, RGB(0, 0, 0)); + DCHECK_NE(res, CLR_INVALID); + res = SetDCBrushColor(context, RGB(255, 255, 255)); + DCHECK_NE(res, CLR_INVALID); + res = SetDCPenColor(context, RGB(0, 0, 0)); + DCHECK_NE(res, CLR_INVALID); + + // Sets up default transparency. + res = SetBkMode(context, OPAQUE); + DCHECK_NE(res, 0); + res = SetROP2(context, R2_COPYPEN); + DCHECK_NE(res, 0); +} + +// static +void PlatformDeviceWin::LoadPathToDC(HDC context, const SkPath& path) { + switch (path.getFillType()) { + case SkPath::kWinding_FillType: { + int res = SetPolyFillMode(context, WINDING); + DCHECK(res != 0); + break; + } + case SkPath::kEvenOdd_FillType: { + int res = SetPolyFillMode(context, ALTERNATE); + DCHECK(res != 0); + break; + } + default: { + NOTREACHED(); + break; + } + } + BOOL res = BeginPath(context); + DCHECK(res != 0); + + CubicPaths paths; + if (!SkPathToCubicPaths(&paths, path)) + return; + + std::vector<POINT> points; + for (CubicPaths::const_iterator path(paths.begin()); path != paths.end(); + ++path) { + if (!path->size()) + continue; + // DCHECK_EQ(points.size() % 4, 0); + 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])); + } + DCHECK_EQ((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())); + DCHECK_NE(res, 0); + if (res == 0) + break; + } + if (res == 0) { + // Make sure the path is discarded. + AbortPath(context); + } else { + res = EndPath(context); + DCHECK(res != 0); + } +} + +// static +void PlatformDeviceWin::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 PlatformDeviceWin::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; + } + case SkPath::kDone_Verb: // iter.next returns 0 points + default: { + current_path = NULL; + // Will return false. + break; + } + } + DCHECK(current_path); + if (!current_path) { + paths->clear(); + return false; + } + current_path->push_back(points_to_add); + } + return true; +} + +// static +void PlatformDeviceWin::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()) { + // Do the transformation. + SkRect rect; + rect.set(region.getBounds()); + transformation.mapRect(&rect); + SkIRect irect; + rect.round(&irect); + hrgn = CreateRectRgnIndirect(&SkIRectToRECT(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); + LoadPathToDC(context, path); + hrgn = PathToRegion(context); + } + int result = SelectClipRgn(context, hrgn); + DCHECK_NE(result, ERROR); + result = DeleteObject(hrgn); + DCHECK_NE(result, 0); +} + +} // namespace gfx + diff --git a/webkit/port/platform/graphics/skia/public/PlatformDeviceWin.h b/webkit/port/platform/graphics/skia/public/PlatformDeviceWin.h new file mode 100644 index 0000000..48b7197 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/PlatformDeviceWin.h @@ -0,0 +1,98 @@ +// 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 PlatformDeviceWin_h +#define PlatformDeviceWin_h + +#include <windows.h> + +#include <vector> + +#include "SkDevice.h" + +class SkMatrix; +class SkPath; +class SkRegion; + +namespace gfx { + +// 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. 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. +class PlatformDeviceWin : public SkDevice { + public: + // 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 HDC getBitmapDC() = 0; + + // 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 drawToHDC(HDC dc, int x, int y, const RECT* src_rect) = 0; + + // Invoke before using GDI functions. See description in platform_device.cc + // for specifics. + // NOTE: x,y,width and height are relative to the current transform. + virtual void prepareForGDI(int x, int y, int width, int height) { } + + // Invoke after using GDI functions. See description in platform_device.cc + // for specifics. + // NOTE: x,y,width and height are relative to the current transform. + virtual void postProcessGDI(int x, int y, int width, int height) { } + + // Sets the opacity of each pixel in the specified region to be opaque. + virtual void makeOpaque(int x, int y, int width, int height) { } + + // Call this function to fix the alpha channels before compositing this layer + // onto another. Internally, the device uses a special alpha method to work + // around problems with Windows. This call will put the values into what + // Skia expects, so it can be composited onto other layers. + // + // After this call, no more drawing can be done because the + // alpha channels will be "correct", which, if this function is called again + // will make them wrong. See the implementation for more discussion. + virtual void fixupAlphaBeforeCompositing() { } + + // Returns if the preferred rendering engine is vectorial or bitmap based. + virtual bool IsVectorial() = 0; + + // Initializes the default settings and colors in a device context. + static void InitializeDC(HDC context); + + // Loads a SkPath into the GDI context. The path can there after be used for + // clipping or as a stroke. + static void LoadPathToDC(HDC context, const SkPath& path); + + // Loads a SkRegion into the GDI context. + static void LoadClippingRegionToDC(HDC context, const SkRegion& region, + const SkMatrix& transformation); + + protected: + // Arrays must be inside structures. + struct CubicPoints { + SkPoint p[4]; + }; + typedef std::vector<CubicPoints> CubicPath; + typedef std::vector<CubicPath> CubicPaths; + + // Forwards |bitmap| to SkDevice's constructor. + PlatformDeviceWin(const SkBitmap& bitmap); + + // 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); +}; + +} // namespace gfx + +#endif // PlatformDeviceWin_h + diff --git a/webkit/port/platform/graphics/skia/public/README b/webkit/port/platform/graphics/skia/public/README new file mode 100644 index 0000000..a0dfcdf --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/README @@ -0,0 +1,3 @@ +The files in this directory must not depend on WebKit types since they are used
+by both the port and the Chromium application layer. They may only depend on
+Skia and the system libraries.
diff --git a/webkit/port/platform/graphics/skia/public/VectorCanvas.cpp b/webkit/port/platform/graphics/skia/public/VectorCanvas.cpp new file mode 100644 index 0000000..440ee64 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/VectorCanvas.cpp @@ -0,0 +1,90 @@ +// 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 "base/gfx/vector_canvas.h" + +#include "base/gfx/vector_device.h" +#include "base/logging.h" + +namespace gfx { + +VectorCanvas::VectorCanvas() { +} + +VectorCanvas::VectorCanvas(HDC dc, int width, int height) { + bool initialized = initialize(dc, width, height); + CHECK(initialized); +} + +VectorCanvas::~VectorCanvas() { +} + +bool VectorCanvas::initialize(HDC context, int width, int height) { + SkDevice* device = createPlatformDevice(width, height, true, context); + if (!device) + return false; + + setDevice(device); + device->unref(); // was created with refcount 1, and setDevice also refs + return true; +} + +SkBounder* VectorCanvas::setBounder(SkBounder* bounder) { + if (!IsTopDeviceVectorial()) + return PlatformCanvasWin::setBounder(bounder); + + // This function isn't used in the code. Verify this assumption. + NOTREACHED(); + return NULL; +} + +SkDevice* VectorCanvas::createDevice(SkBitmap::Config config, + int width, int height, + bool is_opaque, bool isForLayer) { + DCHECK(config == SkBitmap::kARGB_8888_Config); + return createPlatformDevice(width, height, is_opaque, NULL); +} + +SkDrawFilter* VectorCanvas::setDrawFilter(SkDrawFilter* filter) { + // This function isn't used in the code. Verify this assumption. + NOTREACHED(); + return NULL; +} + +SkDevice* VectorCanvas::createPlatformDevice(int width, + int height, bool is_opaque, + HANDLE shared_section) { + if (!is_opaque) { + // TODO(maruel): http://b/1184002 1184002 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 + // BitmapPlatformDeviceWin 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 PlatformCanvasWin::createPlatformDevice(width, height, is_opaque, + shared_section); + } + + // TODO(maruel): http://b/1183870 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. + DCHECK(shared_section); + PlatformDeviceWin* device = VectorDevice::create( + reinterpret_cast<HDC>(shared_section), width, height); + return device; +} + +bool VectorCanvas::IsTopDeviceVectorial() const { + return getTopPlatformDevice().IsVectorial(); +} + +} // namespace gfx + diff --git a/webkit/port/platform/graphics/skia/public/VectorCanvas.h b/webkit/port/platform/graphics/skia/public/VectorCanvas.h new file mode 100644 index 0000000..8538366 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/VectorCanvas.h @@ -0,0 +1,46 @@ +// 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 BASE_GFX_VECTOR_CANVAS_H_ +#define BASE_GFX_VECTOR_CANVAS_H_ + +#include "base/gfx/platform_canvas_win.h" +#include "base/gfx/vector_device.h" + +namespace gfx { + +// 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 VectorCanvas : public PlatformCanvasWin { + public: + VectorCanvas(); + VectorCanvas(HDC dc, int width, int height); + virtual ~VectorCanvas(); + + // For two-part init, call if you use the no-argument constructor above + bool initialize(HDC context, int width, int height); + + virtual SkBounder* setBounder(SkBounder*); + virtual SkDevice* createDevice(SkBitmap::Config config, + int width, int height, + bool is_opaque, bool isForLayer); + virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter); + + private: + // |is_opaque| is unused. |shared_section| is in fact the HDC used for output. + virtual SkDevice* createPlatformDevice(int width, int height, bool is_opaque, + HANDLE shared_section); + + // Returns true if the top device is vector based and not bitmap based. + bool IsTopDeviceVectorial() const; + + DISALLOW_COPY_AND_ASSIGN(VectorCanvas); +}; + +} // namespace gfx + +#endif // BASE_GFX_VECTOR_CANVAS_H_ + diff --git a/webkit/port/platform/graphics/skia/public/VectorCanvas_unittest.cpp b/webkit/port/platform/graphics/skia/public/VectorCanvas_unittest.cpp new file mode 100644 index 0000000..28adff7 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/VectorCanvas_unittest.cpp @@ -0,0 +1,1009 @@ +// 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 "VectorCanvas.h" + +#include <vector> + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/gfx/gdi_util.h" +#include "base/gfx/png_decoder.h" +#include "base/gfx/png_encoder.h" +#include "base/gfx/size.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "base/win_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "SkDashPathEffect.h" + +namespace { + +const wchar_t* const kGenerateSwitch = L"vector-canvas-generate"; + +// Base class for unit test that uses data. It initializes a directory path +// based on the test's name. +class DataUnitTest : public testing::Test { + public: + DataUnitTest(const std::wstring& base_path) : base_path_(base_path) { } + + protected: + // Load the test's data path. + virtual void SetUp() { + const testing::TestInfo& test_info = + *testing::UnitTest::GetInstance()->current_test_info(); + PathService::Get(base::DIR_SOURCE_ROOT, &test_dir_); + file_util::AppendToPath(&test_dir_, base_path_); + file_util::AppendToPath(&test_dir_, L"data"); + file_util::AppendToPath(&test_dir_, + ASCIIToWide(test_info.test_case_name())); + file_util::AppendToPath(&test_dir_, ASCIIToWide(test_info.name())); + + // Hack for a quick lowercase. We assume all the tests names are ASCII. + std::string tmp(WideToASCII(test_dir_)); + for (size_t i = 0; i < tmp.size(); ++i) + tmp[i] = ToLowerASCII(tmp[i]); + test_dir_ = ASCIIToWide(tmp); + } + + // Returns the fully qualified path of directory containing test data files. + const std::wstring& test_dir() const { + return test_dir_; + } + + // Returns the fully qualified path of a data file. + std::wstring test_file(const std::wstring& filename) const { + // Hack for a quick lowercase. We assume all the test data file names are + // ASCII. + std::string tmp(WideToASCII(filename)); + for (size_t i = 0; i < tmp.size(); ++i) + tmp[i] = ToLowerASCII(tmp[i]); + + std::wstring path(test_dir()); + file_util::AppendToPath(&path, ASCIIToWide(tmp)); + return path; + } + + private: + // Path where the unit test is coming from: base, net, chrome, etc. + std::wstring base_path_; + + // Path to directory used to contain the test data. + std::wstring test_dir_; + + DISALLOW_EVIL_CONSTRUCTORS(DataUnitTest); +}; + +// 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_EVIL_CONSTRUCTORS(Context); +}; + +// Lightweight HBITMAP management. +class Bitmap { + public: + Bitmap(const Context& context, int x, int y) { + BITMAPINFOHEADER hdr; + gfx::CreateBitmapHeader(x, y, &hdr); + 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_EVIL_CONSTRUCTORS(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. + Image(const std::wstring& filename) : ignore_alpha_(true) { + std::string compressed; + file_util::ReadFileToString(filename, &compressed); + EXPECT_TRUE(compressed.size()); + + int w; + int h; + EXPECT_TRUE(PNGDecoder::Decode( + reinterpret_cast<const unsigned char*>(compressed.c_str()), + compressed.size(), PNGDecoder::FORMAT_BGRA, &data_, &w, &h)); + size_.SetSize(w, h); + row_length_ = w * sizeof(uint32); + } + + // Loads the image from a canvas. + Image(const gfx::PlatformCanvasWin& canvas) : ignore_alpha_(true) { + // Use a different way to access the bitmap. The normal way would be to + // query the SkBitmap. + HDC context = canvas.getTopPlatformDevice().getBitmapDC(); + 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)); + size_.SetSize(bitmap_data.bmWidth, bitmap_data.bmHeight); + row_length_ = bitmap_data.bmWidthBytes; + size_t size = row_length_ * size_.height(); + data_.resize(size); + memcpy(&*data_.begin(), bitmap_data.bmBits, size); + } + + // Loads the image from a canvas. + Image(const SkBitmap& bitmap) : ignore_alpha_(true) { + SkAutoLockPixels lock(bitmap); + size_.SetSize(bitmap.width(), bitmap.height()); + row_length_ = static_cast<int>(bitmap.rowBytes()); + size_t size = row_length_ * size_.height(); + data_.resize(size); + memcpy(&*data_.begin(), bitmap.getAddr(0, 0), size); + } + + const gfx::Size& size() const { + return size_; + } + + int row_length() const { + return row_length_; + } + + // Save the image to a png file. Used to create the initial test files. + void SaveToFile(const std::wstring& filename) { + std::vector<unsigned char> compressed; + ASSERT_TRUE(PNGEncoder::Encode(&*data_.begin(), + PNGEncoder::FORMAT_BGRA, + size_.width(), + size_.height(), + row_length_, + true, + &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 (size_ != rhs.size_ || row_length_ != rhs.row_length_ || + size_.width() == 0 || size_.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 < size_.height(); ++y) { + for (int x = 0; x < size_.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>(size_.width()) * + static_cast<double>(size_.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 < size_.width()); + EXPECT_TRUE(y >= 0 && y < size_.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]; + } + + private: + // Pixel dimensions of the image. + gfx::Size size_; + + // 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_EVIL_CONSTRUCTORS(Image); +}; + +// Base for tests. Capability to process an image. +class ImageTest : public DataUnitTest { + public: + typedef DataUnitTest parent; + + // In what state is the test running. + enum ProcessAction { + GENERATE, + COMPARE, + NOOP, + }; + + ImageTest(const std::wstring& base_path, ProcessAction default_action) + : parent(base_path), + action_(default_action) { + } + + protected: + virtual void SetUp() { + parent::SetUp(); + + if (action_ == GENERATE) { + // Make sure the directory exist. + file_util::CreateDirectory(test_dir()); + } + } + + // 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(const gfx::PlatformCanvasWin& canvas, + std::wstring filename) const { + filename += L".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(const gfx::PlatformCanvasWin& canvas, + const std::wstring& 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(const gfx::PlatformCanvasWin& canvas, + const std::wstring& filename) const { + Image(canvas).SaveToFile(test_file(filename)); + } + + ProcessAction action_; + + DISALLOW_EVIL_CONSTRUCTORS(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 std::wstring& file, SkBitmap* bitmap) { + std::string compressed; + file_util::ReadFileToString(file, &compressed); + EXPECT_TRUE(compressed.size()); + // Extra-lame. If you care, fix it. + std::vector<unsigned char> data; + data.assign(reinterpret_cast<const unsigned char*>(compressed.c_str()), + reinterpret_cast<const unsigned char*>(compressed.c_str() + + compressed.size())); + EXPECT_TRUE(PNGDecoder::Decode(&data, bitmap)); + EXPECT_FALSE(bitmap->isOpaque()); + Premultiply(*bitmap); +} + +} // namespace + +// Streams an image. +inline std::ostream& operator<<(std::ostream& out, const Image& image) { + return out << "Image(" << image.size().width() << ", " + << image.size().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(L"base", 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 gfx::VectorCanvas(context_->context(), size_, size_); + pcanvas_ = new gfx::PlatformCanvasWin(size_, size_, false); + + // Clear white. + vcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode); + pcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::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 std::wstring& filename) { + std::wstring number(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().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. + gfx::VectorCanvas* vcanvas_; + + // Pixel based canvas. + gfx::PlatformCanvasWin* pcanvas_; + + // When true (default), vcanvas_ and pcanvas_ contents are compared and + // verified to be identical. + bool compare_canvas_; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Actual tests + +TEST_F(VectorCanvasTest, Uninitialized) { + // Do a little mubadumba do get uninitialized stuff. + VectorCanvasTest::TearDown(); + + // The goal is not to verify that have the same uninitialized data. + compare_canvas_ = false; + + context_ = new Context(); + bitmap_ = new Bitmap(*context_, size_, size_); + vcanvas_ = new gfx::VectorCanvas(context_->context(), size_, size_); + pcanvas_ = new gfx::PlatformCanvasWin(size_, size_, false); + + // VectorCanvas default initialization is black. + // PlatformCanvas default initialization is almost white 0x01FFFEFD (invalid + // Skia color) in both Debug and Release. See magicTransparencyColor in + // platform_device.cc + EXPECT_EQ(0., ProcessImage(L"empty")); +} + +TEST_F(VectorCanvasTest, BasicDrawing) { + EXPECT_EQ(Image(*vcanvas_).PercentageDifferent(Image(*pcanvas_)), 0.) + << L"clean"; + EXPECT_EQ(0., ProcessImage(L"clean")); + + // Clear white. + { + vcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode); + pcanvas_->drawARGB(255, 255, 255, 255, SkPorterDuff::kSrc_Mode); + } + EXPECT_EQ(0., ProcessImage(L"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(L"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(L"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(L"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(L"drawRect_noop")); + + // Empty again + { + vcanvas_->drawPaint(SkPaint()); + pcanvas_->drawPaint(SkPaint()); + } + EXPECT_EQ(0., ProcessImage(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"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(L"sw-ne")); + + vcanvas_->drawLine(90, 10, 10, 90, paint); + pcanvas_->drawLine(90, 10, 10, 90, paint); + EXPECT_EQ(0., ProcessImage(L"ne-sw")); + + vcanvas_->drawLine(95, 90, 15, 10, paint); + pcanvas_->drawLine(95, 90, 15, 10, paint); + EXPECT_EQ(0., ProcessImage(L"se-nw")); +} + +TEST_F(VectorCanvasTest, 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(L"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(L"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(L"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(L"circle")); + } +} + +TEST_F(VectorCanvasTest, Bitmaps) { + // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests + // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't + // really care about Windows 2000 pixel colors. + if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) + return; + { + SkBitmap bitmap; + LoadPngFileToSkBitmap(test_file(L"bitmap_opaque.png"), &bitmap); + vcanvas_->drawBitmap(bitmap, 13, 3, NULL); + pcanvas_->drawBitmap(bitmap, 13, 3, NULL); + EXPECT_EQ(0., ProcessImage(L"opaque")); + } + + { + SkBitmap bitmap; + LoadPngFileToSkBitmap(test_file(L"bitmap_alpha.png"), &bitmap); + vcanvas_->drawBitmap(bitmap, 5, 15, NULL); + pcanvas_->drawBitmap(bitmap, 5, 15, NULL); + EXPECT_EQ(0., ProcessImage(L"alpha")); + } +} + +TEST_F(VectorCanvasTest, ClippingRect) { + // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests + // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't + // really care about Windows 2000 pixel colors. + if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) + return; + SkBitmap bitmap; + LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); + 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(L"rect")); +} + +TEST_F(VectorCanvasTest, ClippingPath) { + // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests + // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't + // really care about Windows 2000 pixel colors. + if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) + return; + SkBitmap bitmap; + LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); + 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(L"path")); +} + +TEST_F(VectorCanvasTest, ClippingCombined) { + // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests + // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't + // really care about Windows 2000 pixel colors. + if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) + return; + SkBitmap bitmap; + LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); + + 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(L"combined")); +} + +TEST_F(VectorCanvasTest, ClippingIntersect) { + // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests + // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't + // really care about Windows 2000 pixel colors. + if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) + return; + SkBitmap bitmap; + LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); + + 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(L"intersect")); +} + +TEST_F(VectorCanvasTest, ClippingClean) { + // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests + // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't + // really care about Windows 2000 pixel colors. + if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) + return; + SkBitmap bitmap; + LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); + { + SkRegion old_region(pcanvas_->getTotalClip()); + 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(L"clipped")); + vcanvas_->clipRegion(old_region, SkRegion::kReplace_Op); + pcanvas_->clipRegion(old_region, SkRegion::kReplace_Op); + } + { + // 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(L"unclipped")); + } +} + +TEST_F(VectorCanvasTest, Matrix) { + // ICM is enabled on VectorCanvas only on Windows 2000 so bitmap-based tests + // can't compare the pixels between PlatformCanvas and VectorCanvas. We don't + // really care about Windows 2000 pixel colors. + if (win_util::GetWinVersion() <= win_util::WINVERSION_2000) + return; + SkBitmap bitmap; + LoadPngFileToSkBitmap(test_file(L"..\\bitmaps\\bitmap_opaque.png"), &bitmap); + { + vcanvas_->translate(15, 3); + pcanvas_->translate(15, 3); + vcanvas_->drawBitmap(bitmap, 0, 0, NULL); + pcanvas_->drawBitmap(bitmap, 0, 0, NULL); + EXPECT_EQ(0., ProcessImage(L"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(L"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(L"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(L"rotate")); + } +} + diff --git a/webkit/port/platform/graphics/skia/public/VectorDevice.cpp b/webkit/port/platform/graphics/skia/public/VectorDevice.cpp new file mode 100644 index 0000000..2f5e3aa --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/VectorDevice.cpp @@ -0,0 +1,612 @@ +// 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 "VectorDevice.h" + +#include "base/gfx/gdi_util.h" +#include "base/gfx/skia_utils.h" +#include "base/logging.h" +#include "base/scoped_handle.h" + +#include "SkUtils.h" + +namespace gfx { + +VectorDevice* VectorDevice::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, VectorDevice 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 VectorDevice(dc, bitmap); +} + +VectorDevice::VectorDevice(HDC dc, const SkBitmap& bitmap) + : PlatformDeviceWin(bitmap), + hdc_(dc), + previous_brush_(NULL), + previous_pen_(NULL) { + transform_.reset(); +} + +VectorDevice::~VectorDevice() { + DCHECK(previous_brush_ == NULL); + DCHECK(previous_pen_ == NULL); +} + + +void VectorDevice::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 VectorDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, + size_t count, const SkPoint pts[], + const SkPaint& paint) { + if (!count) + return; + + if (mode == SkCanvas::kPoints_PointMode) { + NOTREACHED(); + 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) { + NOTREACHED(); + 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: + NOTREACHED(); + return; + } + // Draw the calculated path. + drawPath(draw, path, tmp_paint); +} + +void VectorDevice::drawRect(const SkDraw& draw, const SkRect& rect, + const SkPaint& paint) { + if (paint.getPathEffect()) { + // Draw a path instead. + SkPath path_orginal; + path_orginal.addRect(rect); + + // Apply the path effect to the rect. + SkPath path_modified; + paint.getFillPath(path_orginal, &path_modified); + + // Removes the path effect from the temporary SkPaint object. + SkPaint paint_no_effet(paint); + paint_no_effet.setPathEffect(NULL)->safeUnref(); + + // Draw the calculated path. + drawPath(draw, path_modified, paint_no_effet); + return; + } + + if (!ApplyPaint(paint)) { + return; + } + HDC dc = getBitmapDC(); + if (!Rectangle(dc, SkScalarRound(rect.fLeft), + SkScalarRound(rect.fTop), + SkScalarRound(rect.fRight), + SkScalarRound(rect.fBottom))) { + NOTREACHED(); + } + Cleanup(); +} + +void VectorDevice::drawPath(const SkDraw& draw, const SkPath& path, + const SkPaint& paint) { + if (paint.getPathEffect()) { + // Apply the path effect forehand. + SkPath path_modified; + paint.getFillPath(path, &path_modified); + + // Removes the path effect from the temporary SkPaint object. + SkPaint paint_no_effet(paint); + paint_no_effet.setPathEffect(NULL)->safeUnref(); + + // Draw the calculated path. + drawPath(draw, path_modified, paint_no_effet); + return; + } + + if (!ApplyPaint(paint)) { + return; + } + HDC dc = getBitmapDC(); + PlatformDeviceWin::LoadPathToDC(dc, path); + switch (paint.getStyle()) { + case SkPaint::kFill_Style: { + BOOL res = StrokeAndFillPath(dc); + DCHECK(res != 0); + break; + } + case SkPaint::kStroke_Style: { + BOOL res = StrokePath(dc); + DCHECK(res != 0); + break; + } + case SkPaint::kStrokeAndFill_Style: { + BOOL res = StrokeAndFillPath(dc); + DCHECK(res != 0); + break; + } + default: + NOTREACHED(); + break; + } + Cleanup(); +} + +void VectorDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint) { + // Load the temporary matrix. This is what will translate, rotate and resize + // the bitmap. + SkMatrix actual_transform(transform_); + actual_transform.preConcat(matrix); + LoadTransformToDC(hdc_, actual_transform); + + InternalDrawBitmap(bitmap, 0, 0, paint); + + // Restore the original matrix. + LoadTransformToDC(hdc_, transform_); +} + +void VectorDevice::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_); +} + +void VectorDevice::drawText(const SkDraw& draw, const void* text, size_t byteLength, + SkScalar x, SkScalar y, const SkPaint& paint) { + // This function isn't used in the code. Verify this assumption. + NOTREACHED(); +} + +void VectorDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint) { + // This function isn't used in the code. Verify this assumption. + NOTREACHED(); +} + +void VectorDevice::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. + NOTREACHED(); +} + +void VectorDevice::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. + NOTREACHED(); +} + +void VectorDevice::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 VectorDevice::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 + // DCHECK(!paint.getShader()); + + // http://b/1106647 Implement loopers and mask filter. Looper currently in + // use: + // SkBlurDrawLooper is used for shadows. + // DCHECK(!paint.getLooper()); + // DCHECK(!paint.getMaskFilter()); + + // http://b/1165900 Implement xfermode. + // DCHECK(!paint.getXfermode()); + + // The path effect should be processed before arriving here. + DCHECK(!paint.getPathEffect()); + + // These aren't used in the code. Verify this assumption. + DCHECK(!paint.getColorFilter()); + DCHECK(!paint.getRasterizer()); + // Reuse code to load Win32 Fonts. + DCHECK(!paint.getTypeface()); + return true; +} + +void VectorDevice::setMatrixClip(const SkMatrix& transform, + const SkRegion& region) { + transform_ = transform; + LoadTransformToDC(hdc_, transform_); + clip_region_ = region; + if (!clip_region_.isEmpty()) + LoadClipRegion(); +} + +void VectorDevice::drawToHDC(HDC dc, int x, int y, const RECT* src_rect) { + NOTREACHED(); +} + +void VectorDevice::LoadClipRegion() { + SkMatrix t; + t.reset(); + LoadClippingRegionToDC(hdc_, clip_region_, t); +} + +bool VectorDevice::CreateBrush(bool use_brush, COLORREF color) { + DCHECK(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)) { + NOTREACHED(); + return false; + } + + // Select the NULL brush. + previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH)); + return previous_brush_ != NULL; + } + + // Set the opacity. + if (0 == SetBkMode(hdc_, OPAQUE)) { + NOTREACHED(); + return false; + } + + // Create and select the brush. + previous_brush_ = SelectObject(CreateSolidBrush(color)); + return previous_brush_ != NULL; +} + +bool VectorDevice::CreatePen(bool use_pen, COLORREF color, int stroke_width, + float stroke_miter, DWORD pen_style) { + DCHECK(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); + DCHECK(pen != NULL); + previous_pen_ = SelectObject(pen); + if (previous_pen_ == NULL) + return false; + + if (!SetMiterLimit(hdc_, stroke_miter, NULL)) { + NOTREACHED(); + return false; + } + return true; +} + +void VectorDevice::Cleanup() { + if (previous_brush_) { + HGDIOBJ result = SelectObject(previous_brush_); + previous_brush_ = NULL; + if (result) { + BOOL res = DeleteObject(result); + DCHECK(res != 0); + } + } + if (previous_pen_) { + HGDIOBJ result = SelectObject(previous_pen_); + previous_pen_ = NULL; + if (result) { + BOOL res = DeleteObject(result); + DCHECK(res != 0); + } + } + // Remove any loaded path from the context. + AbortPath(hdc_); +} + +HGDIOBJ VectorDevice::SelectObject(HGDIOBJ object) { + HGDIOBJ result = ::SelectObject(hdc_, object); + DCHECK(result != HGDI_ERROR); + if (result == HGDI_ERROR) + return NULL; + return result; +} + +bool VectorDevice::CreateBrush(bool use_brush, const SkPaint& paint) { + // Make sure that for transparent color, no brush is used. + if (paint.getAlpha() == 0) { + // Test if it ever happen. + NOTREACHED(); + use_brush = false; + } + + return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor())); +} + +bool VectorDevice::CreatePen(bool use_pen, const SkPaint& paint) { + // Make sure that for transparent color, no pen is used. + if (paint.getAlpha() == 0) { + // Test if it ever happen. + NOTREACHED(); + 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: + NOTREACHED(); + 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: + NOTREACHED(); + break; + } + + return CreatePen(use_pen, + SkColorToCOLORREF(paint.getColor()), + SkScalarRound(paint.getStrokeWidth()), + paint.getStrokeMiter(), + pen_style); +} + +void VectorDevice::InternalDrawBitmap(const SkBitmap& bitmap, int x, int y, + const SkPaint& paint) { + uint8 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. + BITMAPV4HEADER bitmap_header; + gfx::CreateBitmapV4Header(src_size_x, src_size_y, &bitmap_header); + HDC dc = getBitmapDC(); + SkAutoLockPixels lock(bitmap); + DCHECK_EQ(bitmap.getConfig(), SkBitmap::kARGB_8888_Config); + const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels()); + if (pixels == NULL) { + NOTREACHED(); + 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; + } + } + } + } + + BITMAPINFOHEADER hdr; + gfx::CreateBitmapHeader(src_size_x, src_size_y, &hdr); + if (is_translucent) { + // The image must be loaded as a bitmap inside a device context. + ScopedHDC bitmap_dc(::CreateCompatibleDC(dc)); + void* bits = NULL; + ScopedBitmap hbitmap(::CreateDIBSection( + bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr), + DIB_RGB_COLORS, &bits, NULL, 0)); + memcpy(bits, pixels, bitmap.getSize()); + DCHECK(hbitmap); + HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap); + DeleteObject(old_bitmap); + + // 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); + DCHECK(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); + DCHECK(result); + result = SetStretchBltMode(dc, previous_mode); + DCHECK(result); + } else { + BOOL result = 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); + DCHECK(result); + } + Cleanup(); +} + +} // namespace gfx + diff --git a/webkit/port/platform/graphics/skia/public/VectorDevice.h b/webkit/port/platform/graphics/skia/public/VectorDevice.h new file mode 100644 index 0000000..8579689 --- /dev/null +++ b/webkit/port/platform/graphics/skia/public/VectorDevice.h @@ -0,0 +1,119 @@ +// 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 VectorDevice_h +#define VectorDevice_h + +#include "base/basictypes.h" +#include "base/gfx/platform_device_win.h" +#include "SkMatrix.h" +#include "SkRegion.h" + +namespace gfx { + +// 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 VectorDevice : public PlatformDeviceWin { + public: + // Factory function. The DC is kept as the output context. + static VectorDevice* create(HDC dc, int width, int height); + + VectorDevice(HDC dc, const SkBitmap& bitmap); + virtual ~VectorDevice(); + + virtual HDC getBitmapDC() { + return hdc_; + } + + virtual void drawPaint(const SkDraw& draw, const SkPaint& paint); + virtual void drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, + size_t count, const SkPoint[], const SkPaint& paint); + virtual void drawRect(const SkDraw& draw, const SkRect& r, + const SkPaint& paint); + virtual void drawPath(const SkDraw& draw, const SkPath& path, + const SkPaint& paint); + virtual void drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, + const SkMatrix& matrix, const SkPaint& paint); + virtual void drawSprite(const SkDraw& draw, const SkBitmap& bitmap, + int x, int y, const SkPaint& paint); + virtual void drawText(const SkDraw& draw, const void* text, size_t len, + SkScalar x, SkScalar y, const SkPaint& paint); + virtual void drawPosText(const SkDraw& draw, const void* text, size_t len, + const SkScalar pos[], SkScalar constY, + int scalarsPerPos, const SkPaint& paint); + virtual void drawTextOnPath(const SkDraw& draw, const void* text, size_t len, + const SkPath& path, const SkMatrix* matrix, + const SkPaint& paint); + 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); + virtual void drawDevice(const SkDraw& draw, SkDevice*, int x, int y, + const SkPaint&); + + + virtual void setMatrixClip(const SkMatrix& transform, const SkRegion& region); + virtual void drawToHDC(HDC dc, int x, int y, const RECT* src_rect); + virtual bool IsVectorial() { return true; } + + void LoadClipRegion(); + + 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(VectorDevice); +}; + +} // namespace gfx + +#endif // VectorDevice_h + diff --git a/webkit/tools/test_shell/SConscript b/webkit/tools/test_shell/SConscript index fe47ba7..b12d344 100644 --- a/webkit/tools/test_shell/SConscript +++ b/webkit/tools/test_shell/SConscript @@ -176,6 +176,7 @@ test_files = [ '$WEBKIT_DIR/glue/resource_fetcher_unittest.cc', '$WEBKIT_DIR/glue/webframe_unittest.cc', '$WEBKIT_DIR/port/platform/GKURL_unittest.cpp', + '$WEBKIT_DIR/port/platform/graphics/skia/public/PlatformCanvas_unittest.cpp', '$WEBKIT_DIR/port/platform/image-decoders/bmp/BMPImageDecoder_unittest.cpp', '$WEBKIT_DIR/port/platform/image-decoders/ico/ICOImageDecoder_unittest.cpp', '$WEBKIT_DIR/port/platform/image-decoders/xbm/XBMImageDecoder_unittest.cpp', @@ -190,6 +191,7 @@ if env['PLATFORM'] == 'win32': #'$WEBKIT_DIR/glue/stringimpl_unittest.cc', '$WEBKIT_DIR/glue/webplugin_impl_unittest.cc', '$WEBKIT_DIR/port/platform/graphics/UniscribeHelper_unittest.cpp', + '$WEBKIT_DIR/port/platform/graphics/skia/public/VectorCanvas_unittest.cpp', ]) test_shell_tests = env.ChromeTestProgram('test_shell_tests', diff --git a/webkit/tools/test_shell/test_shell_tests.vcproj b/webkit/tools/test_shell/test_shell_tests.vcproj index 8ca916d..38df275 100644 --- a/webkit/tools/test_shell/test_shell_tests.vcproj +++ b/webkit/tools/test_shell/test_shell_tests.vcproj @@ -367,6 +367,10 @@ > </File> <File + RelativePath="..\..\port\platform\graphics\skia\public\PlatformCanvas_unittest.cpp" + > + </File> + <File RelativePath=".\plugin_tests.cc" > </File> @@ -387,6 +391,10 @@ > </File> <File + RelativePath="..\..\port\platform\graphics\skia\public\VectorCanvas_unittest.cpp" + > + </File> + <File RelativePath="..\..\glue\webframe_unittest.cc" > </File> |