diff options
-rw-r--r-- | webkit/port/platform/graphics/FontWin.cpp | 128 | ||||
-rw-r--r-- | webkit/port/platform/graphics/IconWin.cpp | 7 | ||||
-rw-r--r-- | webkit/port/platform/graphics/ImageSkia.cpp | 244 | ||||
-rw-r--r-- | webkit/port/platform/graphics/PlatformContextSkia.cpp | 370 | ||||
-rw-r--r-- | webkit/port/platform/graphics/PlatformContextSkia.h | 89 | ||||
-rw-r--r-- | webkit/port/rendering/RenderThemeWin.cpp | 87 |
6 files changed, 399 insertions, 526 deletions
diff --git a/webkit/port/platform/graphics/FontWin.cpp b/webkit/port/platform/graphics/FontWin.cpp index 9bd5a7a..0166763 100644 --- a/webkit/port/platform/graphics/FontWin.cpp +++ b/webkit/port/platform/graphics/FontWin.cpp @@ -33,6 +33,7 @@ #include "UniscribeStateTextRun.h" #include "base/gfx/platform_canvas_win.h" +#include "base/gfx/skia_utils.h" #include "graphics/SkiaUtils.h" #include "webkit/glue/webkit_glue.h" @@ -53,6 +54,24 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, // Default size for the buffer. It should be enough for most of cases. const int kDefaultBufferLength = 256; + SkColor color = context->fillColor(); + unsigned char alpha = SkColorGetA(color); + // Skip 100% transparent text; no need to draw anything. + if (!alpha) + return; + + // Set up our graphics context. + HDC hdc = context->canvas()->beginPlatformPaint(); + HGDIOBJ oldFont = SelectObject(hdc, font->platformData().hfont()); + + // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. + // Enforce non-transparent color. + color = SkColorSetRGB(SkColorGetR(color), + SkColorGetG(color), + SkColorGetB(color)); + SetTextColor(hdc, gfx::SkColorToCOLORREF(color)); + SetBkMode(hdc, TRANSPARENT); + // Windows needs the characters and the advances in nice contiguous // buffers, which we build here. Vector<WORD, kDefaultBufferLength> glyphs; @@ -63,48 +82,52 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, // lowest-level text output function on Windows, there should be little // penalty for splitting up the text. On the other hand, the buffer cannot // be bigger than 4094 or the function will fail. - int glyph_index = 0; - int chunk_x = 0; // x offset of this span from the point - while (glyph_index < numGlyphs) { - // how many chars will be in this chunk? - int cur_len = std::min(kMaxBufferLength, numGlyphs - glyph_index); - - glyphs.resize(cur_len); - advances.resize(cur_len); - - int cur_width = 0; - for (int i = 0; i < cur_len; ++i, ++glyph_index) { - glyphs[i] = glyphBuffer.glyphAt(from + glyph_index); - advances[i] = static_cast<int>(glyphBuffer.advanceAt(from + - glyph_index)); - cur_width += advances[i]; - } - - // the 'point' represents the baseline, so we need to move it up to the - // top of the bounding square by subtracting the ascent - SkPoint origin2 = point; - origin2.fY -= font->ascent(); - origin2.fX += chunk_x; - - bool success = false; - for (int executions = 0; executions < 2; ++executions) { - success = context->paintText(font->platformData().hfont(), - cur_len, - &glyphs[0], - &advances[0], - origin2); - if (!success && executions == 0) { - // Ask the browser to load the font for us and retry. - webkit_glue::EnsureFontLoaded(font->platformData().hfont()); - continue; + int glyphIndex = 0; + int chunkX = 0; // x offset of this span from the point + while (glyphIndex < numGlyphs) { + // how many chars will be in this chunk? + int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); + + glyphs.resize(curLen); + advances.resize(curLen); + + int curWidth = 0; + for (int i = 0; i < curLen; ++i, ++glyphIndex) { + glyphs[i] = glyphBuffer.glyphAt(from + glyphIndex); + advances[i] = + static_cast<int>(glyphBuffer.advanceAt(from + glyphIndex)); + curWidth += advances[i]; + } + + SkPoint origin2 = point; + origin2.fX += chunkX; + + bool success = false; + for (int executions = 0; executions < 2; ++executions) { + // The 'origin' represents the baseline, so we need to move it up + // to the top of the bounding square by subtracting the ascent + success = !!ExtTextOut(hdc, static_cast<int>(origin2.fX), + static_cast<int>(origin2.fY) - ascent(), + ETO_GLYPH_INDEX, NULL, + reinterpret_cast<const wchar_t*>(&glyphs[0]), + curLen, + &advances[0]); + + if (!success && executions == 0) { + // Ask the browser to load the font for us and retry. + webkit_glue::EnsureFontLoaded(font->platformData().hfont()); + continue; + } + break; } - break; - } - ASSERT(success); + ASSERT(success); - chunk_x += cur_width; + chunkX += curWidth; } + + SelectObject(hdc, oldFont); + context->canvas()->endPlatformPaint(); } FloatRect Font::selectionRectForComplexText(const TextRun& run, @@ -126,14 +149,39 @@ FloatRect Font::selectionRectForComplexText(const TextRun& run, left - right, static_cast<float>(h)); } -void Font::drawComplexText(GraphicsContext* context, +void Font::drawComplexText(GraphicsContext* graphicsContext, const TextRun& run, const FloatPoint& point, int from, int to) const { + PlatformGraphicsContext* context = graphicsContext->platformContext(); UniscribeStateTextRun state(run, *this); - context->platformContext()->paintComplexText(state, point, from, to, ascent()); + + SkColor color = context->fillColor(); + uint8 alpha = SkColorGetA(color); + // Skip 100% transparent text; no need to draw anything. + if (!alpha) + return; + + HDC hdc = context->canvas()->beginPlatformPaint(); + + // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. + // Enforce non-transparent color. + color = SkColorSetRGB(SkColorGetR(color), + SkColorGetG(color), + SkColorGetB(color)); + SetTextColor(hdc, gfx::SkColorToCOLORREF(color)); + SetBkMode(hdc, TRANSPARENT); + + // Uniscribe counts the coordinates from the upper left, while WebKit uses + // the baseline, so we have to subtract off the ascent. + state.Draw(hdc, + static_cast<int>(point.x()), + static_cast<int>(point.y() - ascent()), + from, + to); + context->canvas()->endPlatformPaint(); } float Font::floatWidthForComplexText(const TextRun& run) const diff --git a/webkit/port/platform/graphics/IconWin.cpp b/webkit/port/platform/graphics/IconWin.cpp index 1856add..b1a8d70 100644 --- a/webkit/port/platform/graphics/IconWin.cpp +++ b/webkit/port/platform/graphics/IconWin.cpp @@ -54,12 +54,15 @@ PassRefPtr<Icon> Icon::newIconForFile(const String& filename) return adoptRef(new Icon(sfi.hIcon)); } -void Icon::paint(GraphicsContext* context, const IntRect& r) +void Icon::paint(GraphicsContext* context, const IntRect& rect) { if (context->paintingDisabled()) return; - context->platformContext()->paintIcon(m_icon, r); + HDC hdc = context->platformContext()->canvas()->beginPlatformPaint(); + DrawIconEx(hdc, rect.x(), rect.y(), m_icon, rect.width(), rect.height(), + 0, 0, DI_NORMAL); + context->platformContext()->canvas()->endPlatformPaint(); } } // namespace WebCore diff --git a/webkit/port/platform/graphics/ImageSkia.cpp b/webkit/port/platform/graphics/ImageSkia.cpp index 5cf364d..5ffd3e3 100644 --- a/webkit/port/platform/graphics/ImageSkia.cpp +++ b/webkit/port/platform/graphics/ImageSkia.cpp @@ -60,6 +60,218 @@ namespace WebCore { namespace { +// Used by computeResamplingMode to tell how bitmaps should be resampled. +enum ResamplingMode { + // Nearest neighbor resampling. Used when we detect that the page is + // trying to make a pattern by stretching a small bitmap very large. + RESAMPLE_NONE, + + // Default skia resampling. Used for large growing of images where high + // quality resampling doesn't get us very much except a slowdown. + RESAMPLE_LINEAR, + + // High quality resampling. + RESAMPLE_AWESOME, +}; + +// static +ResamplingMode computeResamplingMode(const NativeImageSkia& bitmap, + int srcWidth, int srcHeight, + float destWidth, float destHeight) +{ + int destIWidth = static_cast<int>(destWidth); + int destIHeight = static_cast<int>(destHeight); + + // The percent change below which we will not resample. This usually means + // an off-by-one error on the web page, and just doing nearest neighbor + // sampling is usually good enough. + const float kFractionalChangeThreshold = 0.025f; + + // Images smaller than this in either direction are considered "small" and + // are not resampled ever (see below). + const int kSmallImageSizeThreshold = 8; + + // The amount an image can be stretched in a single direction before we + // say that it is being stretched so much that it must be a line or + // background that doesn't need resampling. + const float kLargeStretch = 3.0f; + + // Figure out if we should resample this image. We try to prune out some + // common cases where resampling won't give us anything, since it is much + // slower than drawing stretched. + if (srcWidth == destIWidth && srcHeight == destIHeight) { + // We don't need to resample if the source and destination are the same. + return RESAMPLE_NONE; + } + + if (srcWidth <= kSmallImageSizeThreshold || + srcHeight <= kSmallImageSizeThreshold || + destWidth <= kSmallImageSizeThreshold || + destHeight <= kSmallImageSizeThreshold) { + // Never resample small images. These are often used for borders and + // rules (think 1x1 images used to make lines). + return RESAMPLE_NONE; + } + + if (srcHeight * kLargeStretch <= destHeight || + srcWidth * kLargeStretch <= destWidth) { + // Large image detected. + + // Don't resample if it is being stretched a lot in only one direction. + // This is trying to catch cases where somebody has created a border + // (which might be large) and then is stretching it to fill some part + // of the page. + if (srcWidth == destWidth || srcHeight == destHeight) + return RESAMPLE_NONE; + + // The image is growing a lot and in more than one direction. Resampling + // is slow and doesn't give us very much when growing a lot. + return RESAMPLE_LINEAR; + } + + if ((fabs(destWidth - srcWidth) / srcWidth < + kFractionalChangeThreshold) && + (fabs(destHeight - srcHeight) / srcHeight < + kFractionalChangeThreshold)) { + // It is disappointingly common on the web for image sizes to be off by + // one or two pixels. We don't bother resampling if the size difference + // is a small fraction of the original size. + return RESAMPLE_NONE; + } + + // When the image is not yet done loading, use linear. We don't cache the + // partially resampled images, and as they come in incrementally, it causes + // us to have to resample the whole thing every time. + if (!bitmap.isDataComplete()) + return RESAMPLE_LINEAR; + + // Everything else gets resampled. + return RESAMPLE_AWESOME; +} + +// Draws the given bitmap to the given canvas. The subset of the source bitmap +// identified by src_rect is drawn to the given destination rect. The bitmap +// will be resampled to resample_width * resample_height (this is the size of +// the whole image, not the subset). See shouldResampleBitmap for more. +// +// This does a lot of computation to resample only the portion of the bitmap +// that will only be drawn. This is critical for performance since when we are +// scrolling, for example, we are only drawing a small strip of the image. +// Resampling the whole image every time is very slow, so this speeds up things +// dramatically. +void drawResampledBitmap(SkCanvas& canvas, + SkPaint& paint, + const NativeImageSkia& bitmap, + const SkIRect& srcIRect, + const SkRect& destRect) +{ + // First get the subset we need. This is efficient and does not copy pixels. + SkBitmap subset; + bitmap.extractSubset(&subset, srcIRect); + SkRect srcRect; + srcRect.set(srcIRect); + + // Whether we're doing a subset or using the full source image. + bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0 && + srcIRect.width() == bitmap.width() && + srcIRect.height() == bitmap.height(); + + // We will always draw in integer sizes, so round the destination rect. + SkIRect destRectRounded; + destRect.round(&destRectRounded); + SkIRect resizedImageRect; // Represents the size of the resized image. + resizedImageRect.set(0, 0, + destRectRounded.width(), destRectRounded.height()); + + if (srcIsFull && + bitmap.hasResizedBitmap(destRectRounded.width(), + destRectRounded.height())) { + // Yay, this bitmap frame already has a resized version. + SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), + destRectRounded.height()); + canvas.drawBitmapRect(resampled, 0, destRect, &paint); + return; + } + + // Compute the visible portion of our rect. + SkRect destBitmapSubsetSk; + ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); + destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); + + // The matrix inverting, etc. could have introduced rounding error which + // causes the bounds to be outside of the resized bitmap. We round outward + // so we always lean toward it being larger rather than smaller than we + // need, and then clamp to the bitmap bounds so we don't get any invalid + // data. + SkIRect destBitmapSubsetSkI; + destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); + if (!destBitmapSubsetSkI.intersect(resizedImageRect)) + return; // Resized image does not intersect. + + if (srcIsFull && bitmap.shouldCacheResampling( + resizedImageRect.width(), + resizedImageRect.height(), + destBitmapSubsetSkI.width(), + destBitmapSubsetSkI.height())) { + // We're supposed to resize the entire image and cache it, even though + // we don't need all of it. + SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), + destRectRounded.height()); + canvas.drawBitmapRect(resampled, 0, destRect, &paint); + } else { + // We should only resize the exposed part of the bitmap to do the + // minimal possible work. + gfx::Rect destBitmapSubset(destBitmapSubsetSkI.fLeft, + destBitmapSubsetSkI.fTop, + destBitmapSubsetSkI.width(), + destBitmapSubsetSkI.height()); + + // Resample the needed part of the image. + SkBitmap resampled = gfx::ImageOperations::Resize(subset, + gfx::ImageOperations::RESIZE_LANCZOS3, + gfx::Size(destRectRounded.width(), destRectRounded.height()), + destBitmapSubset); + + // Compute where the new bitmap should be drawn. Since our new bitmap + // may be smaller than the original, we have to shift it over by the + // same amount that we cut off the top and left. + SkRect offsetDestRect = { + destBitmapSubset.x() + destRect.fLeft, + destBitmapSubset.y() + destRect.fTop, + destBitmapSubset.right() + destRect.fLeft, + destBitmapSubset.bottom() + destRect.fTop }; + + canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint); + } +} + +void paintSkBitmap(PlatformContextSkia* platformContext, + const NativeImageSkia& bitmap, + const SkIRect& srcRect, + const SkRect& destRect, + const SkPorterDuff::Mode& compOp) +{ + SkPaint paint; + paint.setPorterDuffXfermode(compOp); + + gfx::PlatformCanvas* canvas = platformContext->canvas(); + + ResamplingMode resampling = platformContext->IsPrinting() ? RESAMPLE_NONE : + computeResamplingMode(bitmap, srcRect.width(), srcRect.height(), + SkScalarToFloat(destRect.width()), + SkScalarToFloat(destRect.height())); + if (resampling == RESAMPLE_AWESOME) { + paint.setFilterBitmap(false); + drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); + } else { + // No resampling necessary, we can just draw the bitmap. + // Note: for serialization, we will want to subset the bitmap first so + // we don't send extra pixels. + paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); + canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint); + } +} + // Transforms the given dimensions with the given matrix. Used to see how big // images will be once transformed. void TransformDimensions(const SkMatrix& matrix, @@ -185,20 +397,19 @@ void Image::drawPattern(GraphicsContext* context, &dest_bitmap_width, &dest_bitmap_height); // Compute the resampling mode. - PlatformContextSkia::ResamplingMode resampling; + ResamplingMode resampling; if (context->platformContext()->IsPrinting()) - resampling = PlatformContextSkia::RESAMPLE_LINEAR; + resampling = RESAMPLE_LINEAR; else { - resampling = PlatformContextSkia::computeResamplingMode( - *bitmap, - srcRect.width(), srcRect.height(), - dest_bitmap_width, dest_bitmap_height); + resampling = computeResamplingMode(*bitmap, + srcRect.width(), srcRect.height(), + dest_bitmap_width, dest_bitmap_height); } // Load the transform WebKit requested. SkMatrix matrix(patternTransform); - if (resampling == PlatformContextSkia::RESAMPLE_AWESOME) { + if (resampling == RESAMPLE_AWESOME) { // Do nice resampling. SkBitmap resampled = gfx::ImageOperations::Resize(src_subset, gfx::ImageOperations::RESIZE_LANCZOS3, @@ -228,7 +439,7 @@ void Image::drawPattern(GraphicsContext* context, SkPaint paint; paint.setShader(shader)->unref(); paint.setPorterDuffXfermode(WebCoreCompositeToSkiaComposite(compositeOp)); - paint.setFilterBitmap(resampling == PlatformContextSkia::RESAMPLE_LINEAR); + paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); context->platformContext()->paintSkPaint(destRect, paint); } @@ -267,8 +478,11 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, if (srcRect.isEmpty() || dstRect.isEmpty()) return; // Nothing to draw. - ctxt->platformContext()->paintSkBitmap(*bm, enclosingIntRect(srcRect), - enclosingIntRect(dstRect), WebCoreCompositeToSkiaComposite(compositeOp)); + paintSkBitmap(ctxt->platformContext(), + *bm, + enclosingIntRect(srcRect), + enclosingIntRect(dstRect), + WebCoreCompositeToSkiaComposite(compositeOp)); startAnimation(); } @@ -281,11 +495,11 @@ void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt, if (srcRect.isEmpty() || dstRect.isEmpty()) return; // Nothing to draw. - ctxt->platformContext()->paintSkBitmap( - m_nativeImage, - enclosingIntRect(srcRect), - enclosingIntRect(dstRect), - WebCoreCompositeToSkiaComposite(compositeOp)); + paintSkBitmap(ctxt->platformContext(), + m_nativeImage, + enclosingIntRect(srcRect), + enclosingIntRect(dstRect), + WebCoreCompositeToSkiaComposite(compositeOp)); } PassRefPtr<BitmapImageSingleFrameSkia> BitmapImageSingleFrameSkia::create( diff --git a/webkit/port/platform/graphics/PlatformContextSkia.cpp b/webkit/port/platform/graphics/PlatformContextSkia.cpp index 3c6b53e..16dbce8 100644 --- a/webkit/port/platform/graphics/PlatformContextSkia.cpp +++ b/webkit/port/platform/graphics/PlatformContextSkia.cpp @@ -43,113 +43,6 @@ #include "SkShader.h" #include "SkDashPathEffect.h" -#if PLATFORM(WIN_OS) -#include <vssym32.h> -#include "base/gfx/native_theme.h" -#include "ThemeData.h" -#include "UniscribeStateTextRun.h" -#endif - -namespace { - -// Draws the given bitmap to the given canvas. The subset of the source bitmap -// identified by src_rect is drawn to the given destination rect. The bitmap -// will be resampled to resample_width * resample_height (this is the size of -// the whole image, not the subset). See shouldResampleBitmap for more. -// -// This does a lot of computation to resample only the portion of the bitmap -// that will only be drawn. This is critical for performance since when we are -// scrolling, for example, we are only drawing a small strip of the image. -// Resampling the whole image every time is very slow, so this speeds up things -// dramatically. -void DrawResampledBitmap(SkCanvas& canvas, - SkPaint& paint, - const NativeImageSkia& bitmap, - const SkIRect& srcIRect, - const SkRect& destRect) -{ - // First get the subset we need. This is efficient and does not copy pixels. - SkBitmap subset; - bitmap.extractSubset(&subset, srcIRect); - SkRect srcRect; - srcRect.set(srcIRect); - - // Whether we're doing a subset or using the full source image. - bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0 && - srcIRect.width() == bitmap.width() && - srcIRect.height() == bitmap.height(); - - // We will always draw in integer sizes, so round the destination rect. - SkIRect destRectRounded; - destRect.round(&destRectRounded); - SkIRect resizedImageRect; // Represents the size of the resized image. - resizedImageRect.set(0, 0, - destRectRounded.width(), destRectRounded.height()); - - if (srcIsFull && - bitmap.hasResizedBitmap(destRectRounded.width(), - destRectRounded.height())) { - // Yay, this bitmap frame already has a resized version. - SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), - destRectRounded.height()); - canvas.drawBitmapRect(resampled, 0, destRect, &paint); - return; - } - - // Compute the visible portion of our rect. - SkRect destBitmapSubsetSk; - ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); - destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); - - // The matrix inverting, etc. could have introduced rounding error which - // causes the bounds to be outside of the resized bitmap. We round outward - // so we always lean toward it being larger rather than smaller than we - // need, and then clamp to the bitmap bounds so we don't get any invalid - // data. - SkIRect destBitmapSubsetSkI; - destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); - if (!destBitmapSubsetSkI.intersect(resizedImageRect)) - return; // Resized image does not intersect. - - if (srcIsFull && bitmap.shouldCacheResampling( - resizedImageRect.width(), - resizedImageRect.height(), - destBitmapSubsetSkI.width(), - destBitmapSubsetSkI.height())) { - // We're supposed to resize the entire image and cache it, even though - // we don't need all of it. - SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), - destRectRounded.height()); - canvas.drawBitmapRect(resampled, 0, destRect, &paint); - } else { - // We should only resize the exposed part of the bitmap to do the - // minimal possible work. - gfx::Rect destBitmapSubset(destBitmapSubsetSkI.fLeft, - destBitmapSubsetSkI.fTop, - destBitmapSubsetSkI.width(), - destBitmapSubsetSkI.height()); - - // Resample the needed part of the image. - SkBitmap resampled = gfx::ImageOperations::Resize(subset, - gfx::ImageOperations::RESIZE_LANCZOS3, - gfx::Size(destRectRounded.width(), destRectRounded.height()), - destBitmapSubset); - - // Compute where the new bitmap should be drawn. Since our new bitmap - // may be smaller than the original, we have to shift it over by the - // same amount that we cut off the top and left. - SkRect offsetDestRect = { - destBitmapSubset.x() + destRect.fLeft, - destBitmapSubset.y() + destRect.fTop, - destBitmapSubset.right() + destRect.fLeft, - destBitmapSubset.bottom() + destRect.fTop }; - - canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint); - } -} - -} // namespace - // State ----------------------------------------------------------------------- // Encapsulates the additional painting state information we store for each @@ -215,9 +108,9 @@ PlatformContextSkia::State::State() PlatformContextSkia::State::State(const State& other) { - other.m_looper->safeRef(); memcpy(this, &other, sizeof(State)); + m_looper->safeRef(); m_dash->safeRef(); m_gradient->safeRef(); m_pattern->safeRef(); @@ -249,26 +142,18 @@ PlatformContextSkia::PlatformContextSkia(gfx::PlatformCanvas* canvas) : m_canvas(canvas) , m_stateStack(sizeof(State)) { - State* state = reinterpret_cast<State*>(m_stateStack.push_back()); - new (state) State(); - m_state = state; + m_stateStack.append(State()); + m_state = &m_stateStack.last(); } PlatformContextSkia::~PlatformContextSkia() { - // We force restores so we don't leak any subobjects owned by our - // stack of State records. - while (m_stateStack.count() > 0) { - reinterpret_cast<State*>(m_stateStack.back())->~State(); - m_stateStack.pop_back(); - } } void PlatformContextSkia::save() { - State* newState = reinterpret_cast<State*>(m_stateStack.push_back()); - new (newState) State(*m_state); - m_state = newState; + m_stateStack.append(State()); + m_state = &m_stateStack.last(); // Save our native canvas. canvas()->save(); @@ -276,13 +161,11 @@ void PlatformContextSkia::save() void PlatformContextSkia::restore() { + m_stateStack.removeLast(); + m_state = &m_stateStack.last(); + // Restore our native canvas. canvas()->restore(); - - m_state->~State(); - m_stateStack.pop_back(); - - m_state = reinterpret_cast<State*>(m_stateStack.back()); } void PlatformContextSkia::drawRect(SkRect rect) @@ -505,249 +388,12 @@ void PlatformContextSkia::setDashPathEffect(SkDashPathEffect* dash) } } -// TODO(brettw) all this platform stuff should be moved out of this class into -// platform-specific files for that type of thing (e.g. to FontWin). -#if PLATFORM(WIN_OS) - -const gfx::NativeTheme* PlatformContextSkia::nativeTheme() -{ - return gfx::NativeTheme::instance(); -} - -// TODO(brettw) move to a platform-specific file. -void PlatformContextSkia::paintIcon(HICON icon, const SkIRect& rect) -{ - HDC hdc = m_canvas->beginPlatformPaint(); - DrawIconEx(hdc, rect.fLeft, rect.fTop, icon, rect.width(), rect.height(), - 0, 0, DI_NORMAL); - m_canvas->endPlatformPaint(); -} - -// RenderThemeWin.cpp -void PlatformContextSkia::paintButton(const SkIRect& widgetRect, - const ThemeData& themeData) -{ - RECT rect(gfx::SkIRectToRECT(widgetRect)); - HDC hdc = m_canvas->beginPlatformPaint(); - int state = themeData.m_state; - nativeTheme()->PaintButton(hdc, - themeData.m_part, - state, - themeData.m_classicState, - &rect); - m_canvas->endPlatformPaint(); -} - -void PlatformContextSkia::paintTextField(const SkIRect& widgetRect, - const ThemeData& themeData, - SkColor c, - bool drawEdges) -{ - RECT rect(gfx::SkIRectToRECT(widgetRect)); - HDC hdc = m_canvas->beginPlatformPaint(); - nativeTheme()->PaintTextField(hdc, - themeData.m_part, - themeData.m_state, - themeData.m_classicState, - &rect, - gfx::SkColorToCOLORREF(c), - true, - drawEdges); - m_canvas->endPlatformPaint(); -} - -void PlatformContextSkia::paintMenuListArrowButton(const SkIRect& widgetRect, - unsigned state, - unsigned classicState) -{ - RECT rect(gfx::SkIRectToRECT(widgetRect)); - HDC hdc = m_canvas->beginPlatformPaint(); - nativeTheme()->PaintMenuList(hdc, - CP_DROPDOWNBUTTON, - state, - classicState, - &rect); - m_canvas->endPlatformPaint(); -} - -void PlatformContextSkia::paintComplexText(UniscribeStateTextRun& state, - const SkPoint& point, - int from, - int to, - int ascent) -{ - SkColor color(fillColor()); - uint8 alpha = SkColorGetA(color); - // Skip 100% transparent text; no need to draw anything. - if (!alpha) - return; - - HDC hdc = m_canvas->beginPlatformPaint(); - - // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. - // Enforce non-transparent color. - color = SkColorSetRGB(SkColorGetR(color), - SkColorGetG(color), - SkColorGetB(color)); - SetTextColor(hdc, gfx::SkColorToCOLORREF(color)); - SetBkMode(hdc, TRANSPARENT); - - // Uniscribe counts the coordinates from the upper left, while WebKit uses - // the baseline, so we have to subtract off the ascent. - state.Draw(hdc, - static_cast<int>(point.fX), - static_cast<int>(point.fY - ascent), - from, - to); - m_canvas->endPlatformPaint(); -} - -// TODO(brettw) move to FontWin -bool PlatformContextSkia::paintText(FontHandle hfont, - int numberGlyph, - const uint16* glyphs, - const int* advances, - const SkPoint& origin) -{ - SkColor color(fillColor()); - uint8 alpha = SkColorGetA(color); - // Skip 100% transparent text; no need to draw anything. - if (!alpha) - return true; - - bool success = false; - HDC hdc = m_canvas->beginPlatformPaint(); - HGDIOBJ oldFont = SelectObject(hdc, hfont); - - // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. - // Enforce non-transparent color. - color = SkColorSetRGB(SkColorGetR(color), - SkColorGetG(color), - SkColorGetB(color)); - SetTextColor(hdc, gfx::SkColorToCOLORREF(color)); - SetBkMode(hdc, TRANSPARENT); - - // The 'origin' represents the baseline, so we need to move it up to the - // top of the bounding square by subtracting the ascent - success = !!ExtTextOut(hdc, static_cast<int>(origin.fX), - static_cast<int>(origin.fY), - ETO_GLYPH_INDEX, 0, - reinterpret_cast<const wchar_t*>(glyphs), - numberGlyph, - advances); - SelectObject(hdc, oldFont); - m_canvas->endPlatformPaint(); - return success; -} - -#endif // PLATFORM(WIN_OS); - void PlatformContextSkia::paintSkPaint(const SkRect& rect, const SkPaint& paint) { m_canvas->drawRect(rect, paint); } -// static -PlatformContextSkia::ResamplingMode PlatformContextSkia::computeResamplingMode( - const NativeImageSkia& bitmap, - int srcWidth, int srcHeight, - float destWidth, float destHeight) -{ - int destIWidth = static_cast<int>(destWidth); - int destIHeight = static_cast<int>(destHeight); - - // The percent change below which we will not resample. This usually means - // an off-by-one error on the web page, and just doing nearest neighbor - // sampling is usually good enough. - const float kFractionalChangeThreshold = 0.025f; - - // Images smaller than this in either direction are considered "small" and - // are not resampled ever (see below). - const int kSmallImageSizeThreshold = 8; - - // The amount an image can be stretched in a single direction before we - // say that it is being stretched so much that it must be a line or - // background that doesn't need resampling. - const float kLargeStretch = 3.0f; - - // Figure out if we should resample this image. We try to prune out some - // common cases where resampling won't give us anything, since it is much - // slower than drawing stretched. - if (srcWidth == destIWidth && srcHeight == destIHeight) { - // We don't need to resample if the source and destination are the same. - return RESAMPLE_NONE; - } - - if (srcWidth <= kSmallImageSizeThreshold || - srcHeight <= kSmallImageSizeThreshold || - destWidth <= kSmallImageSizeThreshold || - destHeight <= kSmallImageSizeThreshold) { - // Never resample small images. These are often used for borders and - // rules (think 1x1 images used to make lines). - return RESAMPLE_NONE; - } - - if (srcHeight * kLargeStretch <= destHeight || - srcWidth * kLargeStretch <= destWidth) { - // Large image detected. - - // Don't resample if it is being stretched a lot in only one direction. - // This is trying to catch cases where somebody has created a border - // (which might be large) and then is stretching it to fill some part - // of the page. - if (srcWidth == destWidth || srcHeight == destHeight) - return RESAMPLE_NONE; - - // The image is growing a lot and in more than one direction. Resampling - // is slow and doesn't give us very much when growing a lot. - return RESAMPLE_LINEAR; - } - - if ((fabs(destWidth - srcWidth) / srcWidth < - kFractionalChangeThreshold) && - (fabs(destHeight - srcHeight) / srcHeight < - kFractionalChangeThreshold)) { - // It is disappointingly common on the web for image sizes to be off by - // one or two pixels. We don't bother resampling if the size difference - // is a small fraction of the original size. - return RESAMPLE_NONE; - } - - // When the image is not yet done loading, use linear. We don't cache the - // partially resampled images, and as they come in incrementally, it causes - // us to have to resample the whole thing every time. - if (!bitmap.isDataComplete()) - return RESAMPLE_LINEAR; - - // Everything else gets resampled. - return RESAMPLE_AWESOME; -} - -void PlatformContextSkia::paintSkBitmap(const NativeImageSkia& bitmap, - const SkIRect& srcRect, - const SkRect& destRect, - const SkPorterDuff::Mode& compOp) -{ - SkPaint paint; - paint.setPorterDuffXfermode(compOp); - - ResamplingMode resampling = IsPrinting() ? RESAMPLE_NONE : - computeResamplingMode(bitmap, srcRect.width(), srcRect.height(), - SkScalarToFloat(destRect.width()), - SkScalarToFloat(destRect.height())); - if (resampling == RESAMPLE_AWESOME) { - paint.setFilterBitmap(false); - DrawResampledBitmap(*m_canvas, paint, bitmap, srcRect, destRect); - } else { - // No resampling necessary, we can just draw the bitmap. - // Note: for serialization, we will want to subset the bitmap first so - // we don't send extra pixels. - paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); - m_canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint); - } -} - const SkBitmap* PlatformContextSkia::bitmap() const { return &m_canvas->getDevice()->accessBitmap(false); diff --git a/webkit/port/platform/graphics/PlatformContextSkia.h b/webkit/port/platform/graphics/PlatformContextSkia.h index ab407bb..ecc56020 100644 --- a/webkit/port/platform/graphics/PlatformContextSkia.h +++ b/webkit/port/platform/graphics/PlatformContextSkia.h @@ -38,27 +38,7 @@ #include "SkPath.h" #include "GraphicsContext.h" -#include "ThemeData.h" - -#if defined(OS_WIN) -typedef HICON IconHandle; -typedef HFONT FontHandle; -#elif defined(OS_MACOSX) -typedef CGImageRef IconHandle; -typedef CTFontRef FontHandle; -#elif defined(OS_LINUX) -// TODO(erg): Type needs to be defined for half the rest of the stack to -// compile. When the corresponding implementation to this file gets written, -// these void pointers need to be replaced with whatever we end up using. -typedef void* IconHandle; -typedef void* FontHandle; -#endif - -class UniscribeStateTextRun; - -namespace gfx { -class NativeTheme; -} +#include "wtf/Vector.h" // This class holds the platform-specific state for GraphicsContext. We put // most of our Skia wrappers on this class. In theory, a lot of this stuff could @@ -79,20 +59,6 @@ class NativeTheme; // state to be pushed and popped along with the bitmap. class PlatformContextSkia { public: - // Used by computeResamplingMode to tell how bitmaps should be resampled. - enum ResamplingMode { - // Nearest neighbor resampling. Used when we detect that the page is - // trying to make a pattern by stretching a small bitmap very large. - RESAMPLE_NONE, - - // Default skia resampling. Used for large growing of images where high - // quality resampling doesn't get us very much except a slowdown. - RESAMPLE_LINEAR, - - // High quality resampling. - RESAMPLE_AWESOME, - }; - // For printing, there shouldn't be any canvas. canvas can be NULL. PlatformContextSkia(gfx::PlatformCanvas* canvas); ~PlatformContextSkia(); @@ -150,60 +116,9 @@ public: // TODO(brettw) why is this in this class? void drawRect(SkRect rect); - // Gets the default theme. - static const gfx::NativeTheme* nativeTheme(); - - void paintIcon(IconHandle icon, const SkIRect& rect); - // TODO(brettw) these functions should not have to take GraphicsContext - // pointers! This is cureently required because our theme drawing code - // requires GraphicsContexts, but this class doesn't know about it. This - // class's purpose seems no longer necessary, so everything can be moved to - // GraphicsContext or ThemeWin, and these parameters can be removed. - void paintButton(const SkIRect& widgetRect, - const ThemeData& themeData); - void paintTextField(const SkIRect& widgetRect, - const ThemeData& themeData, - SkColor c, - bool drawEdges); - void paintMenuListArrowButton(const SkIRect& widgetRect, - unsigned state, - unsigned classicState); - void paintComplexText(UniscribeStateTextRun& state, - const SkPoint& point, - int from, - int to, - int ascent); - - // Draws the given glyphs at the given |origin|. |origin| is on the baseline - // of the text, and the |ascender_height| must be given for some code paths - // since Windows draws text from the upper left. - bool paintText(FontHandle hfont, - int number_glyph, - const uint16* glyphs, - const int* advances, - const SkPoint& origin); - // TODO(maruel): I'm still unsure how I will serialize this call. void paintSkPaint(const SkRect& rect, const SkPaint& paint); - // Draws the given bitmap in the canvas at the location specified in - // |dest_rect|. It will be resampled as necessary to fill that rectangle. - // The |src_rect| indicates the subset of the bitmap to draw. - void paintSkBitmap(const NativeImageSkia& bitmap, - const SkIRect& srcRect, - const SkRect& destRect, - const SkPorterDuff::Mode& compositeOp); - - // Returns true if the given bitmap subset should be resampled before being - // painted into a rectangle of the given size. This is used to indicate - // whether bitmap painting should be optimized by not resampling, or given - // higher quality by resampling. - static ResamplingMode computeResamplingMode(const NativeImageSkia& bitmap, - int srcWidth, - int srcHeight, - float destWidth, - float destHeight); - const SkBitmap* bitmap() const; // Returns the canvas used for painting, NOT guaranteed to be non-NULL. @@ -227,7 +142,7 @@ private: // States stack. Enables local drawing state change with save()/restore() // calls. - SkDeque m_stateStack; + WTF::Vector<State> m_stateStack; // Pointer to the current drawing state. This is a cached value of // mStateStack.back(). State* m_state; diff --git a/webkit/port/rendering/RenderThemeWin.cpp b/webkit/port/rendering/RenderThemeWin.cpp index aeac95d..2bb81ab 100644 --- a/webkit/port/rendering/RenderThemeWin.cpp +++ b/webkit/port/rendering/RenderThemeWin.cpp @@ -36,6 +36,7 @@ #include "base/gfx/native_theme.h" #include "base/gfx/font_utils.h" +#include "base/gfx/skia_utils.h" #include "base/win_util.h" #include "webkit/glue/webkit_glue.h" @@ -475,10 +476,26 @@ ThemeData RenderThemeWin::getThemeData(RenderObject* o) return result; } -bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +bool RenderThemeWin::paintButton(RenderObject* o, + const RenderObject::PaintInfo& i, + const IntRect& r) { - // Get the correct theme data for a button and paint the button. - i.context->platformContext()->paintButton(r, getThemeData(o)); + const ThemeData& themeData = getThemeData(o); + + RECT rect; + rect.left = r.x(); + rect.top = r.y(); + rect.right = r.right(); + rect.bottom = r.bottom(); + + HDC hdc = i.context->platformContext()->canvas()->beginPlatformPaint(); + int state = themeData.m_state; + gfx::NativeTheme::instance()->PaintButton(hdc, + themeData.m_part, + state, + themeData.m_classicState, + &rect); + i.context->platformContext()->canvas()->endPlatformPaint(); return false; } @@ -541,18 +558,39 @@ bool RenderThemeWin::paintTextField(RenderObject* o, const RenderObject::PaintIn return paintTextFieldInternal(o, i, r, true); } -bool RenderThemeWin::paintTextFieldInternal(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r, bool drawEdges) +bool RenderThemeWin::paintTextFieldInternal(RenderObject* o, + const RenderObject::PaintInfo& i, + const IntRect& r, + bool drawEdges) { - // Nasty hack to make us not paint the border on text fields with a border-radius. - // Webkit paints elements with border-radius for us. - // TODO(ojan): Get rid of this if-check once we can properly clip rounded borders - // http://b/1112604 and http://b/1108635 - // TODO(ojan): make sure we do the right thing if css background-clip is set. + // Nasty hack to make us not paint the border on text fields with a + // border-radius. Webkit paints elements with border-radius for us. + // TODO(ojan): Get rid of this if-check once we can properly clip rounded + // borders: http://b/1112604 and http://b/1108635 + // TODO(ojan): make sure we do the right thing if css background-clip is + // set. if (o->style()->hasBorderRadius()) - return false; - - // Get the correct theme data for a textfield and paint the text field. - i.context->platformContext()->paintTextField(r, getThemeData(o), o->style()->backgroundColor().rgb(), drawEdges); + return false; + + const ThemeData& themeData = getThemeData(o); + + RECT rect; + rect.left = r.x(); + rect.top = r.y(); + rect.right = r.right(); + rect.bottom = r.bottom(); + + HDC hdc = i.context->platformContext()->canvas()->beginPlatformPaint(); + COLORREF clr = gfx::SkColorToCOLORREF(o->style()->backgroundColor().rgb()); + gfx::NativeTheme::instance()->PaintTextField(hdc, + themeData.m_part, + themeData.m_state, + themeData.m_classicState, + &rect, + clr, + true, + drawEdges); + i.context->platformContext()->canvas()->endPlatformPaint(); return false; } @@ -619,17 +657,26 @@ bool RenderThemeWin::paintMenuList(RenderObject* o, const RenderObject::PaintInf int buttonX; if (r.right() - r.x() < buttonWidth) { - buttonX = r.x(); + buttonX = r.x(); } else { - buttonX = o->style()->direction() == LTR ? r.right() - spacingRight - buttonWidth : r.x() + spacingLeft; + buttonX = o->style()->direction() == LTR ? r.right() - spacingRight - buttonWidth : r.x() + spacingLeft; } - IntRect buttonRect(buttonX, - r.y() + spacingTop, - std::min(buttonWidth, r.right() - r.x()), - r.height() - (spacingTop + spacingBottom)); + + // Compute the Windows rectangle of the button. + RECT rect; + rect.left = buttonX; + rect.top = r.y() + spacingTop; + rect.right = rect.left + std::min(buttonWidth, r.right() - r.x()); + rect.bottom = rect.top + r.height() - (spacingTop + spacingBottom); // Get the correct theme data for a textfield and paint the menu. - i.context->platformContext()->paintMenuListArrowButton(buttonRect, determineState(o), determineClassicState(o)); + HDC hdc = i.context->platformContext()->canvas()->beginPlatformPaint(); + gfx::NativeTheme::instance()->PaintMenuList(hdc, + CP_DROPDOWNBUTTON, + determineState(o), + determineClassicState(o), + &rect); + i.context->platformContext()->canvas()->endPlatformPaint(); return false; } |