Index: base/gfx/vector_device.cc |
=================================================================== |
--- base/gfx/vector_device.cc (revision 5678) |
+++ base/gfx/vector_device.cc (working copy) |
@@ -1,612 +0,0 @@ |
-// 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_device.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 |
- |