| // Copyright (c) 2010 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 "gfx/canvas_direct2d.h" |
| |
| #include "base/scoped_ptr.h" |
| #include "gfx/brush.h" |
| #include "gfx/rect.h" |
| |
| namespace { |
| |
| // Converts a SkColor to a ColorF. |
| D2D1_COLOR_F SkColorToColorF(SkColor color) { |
| return D2D1::ColorF(static_cast<float>(SkColorGetR(color)) / 0xFF, |
| static_cast<float>(SkColorGetG(color)) / 0xFF, |
| static_cast<float>(SkColorGetB(color)) / 0xFF, |
| static_cast<float>(SkColorGetA(color)) / 0xFF); |
| } |
| |
| D2D1_RECT_F RectToRectF(int x, int y, int w, int h) { |
| return D2D1::RectF(static_cast<float>(x), static_cast<float>(y), |
| static_cast<float>(x + w), static_cast<float>(y + h)); |
| } |
| |
| D2D1_RECT_F RectToRectF(const gfx::Rect& rect) { |
| return RectToRectF(rect.x(), rect.y(), rect.width(), rect.height()); |
| } |
| |
| D2D1_POINT_2F PointToPoint2F(int x, int y) { |
| return D2D1::Point2F(static_cast<float>(x), static_cast<float>(y)); |
| } |
| |
| D2D1_BITMAP_INTERPOLATION_MODE FilterToInterpolationMode(bool filter) { |
| return filter ? D2D1_BITMAP_INTERPOLATION_MODE_LINEAR |
| : D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; |
| } |
| |
| // Creates a Direct2D bitmap object from the contents of a SkBitmap. The caller |
| // is responsible for releasing this object. |
| ID2D1Bitmap* CreateD2D1BitmapFromSkBitmap(ID2D1RenderTarget* render_target, |
| const SkBitmap& bitmap) { |
| ID2D1Bitmap* d2d1_bitmap = NULL; |
| HRESULT hr = render_target->CreateBitmap( |
| D2D1::SizeU(bitmap.width(), bitmap.height()), |
| NULL, |
| NULL, |
| D2D1::BitmapProperties( |
| D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, |
| D2D1_ALPHA_MODE_IGNORE)), |
| &d2d1_bitmap); |
| if (FAILED(hr)) |
| return NULL; |
| bitmap.lockPixels(); |
| d2d1_bitmap->CopyFromMemory(NULL, bitmap.getPixels(), bitmap.rowBytes()); |
| bitmap.unlockPixels(); |
| return d2d1_bitmap; |
| } |
| |
| // Creates a Direct2D bitmap brush from the contents of a SkBitmap. The caller |
| // is responsible for releasing this object. |
| ID2D1Brush* CreateD2D1BrushFromSkBitmap(ID2D1RenderTarget* render_target, |
| const SkBitmap& bitmap, |
| D2D1_EXTEND_MODE extend_mode_x, |
| D2D1_EXTEND_MODE extend_mode_y) { |
| ScopedComPtr<ID2D1Bitmap> d2d1_bitmap( |
| CreateD2D1BitmapFromSkBitmap(render_target, bitmap)); |
| |
| ID2D1BitmapBrush* brush = NULL; |
| render_target->CreateBitmapBrush( |
| d2d1_bitmap, |
| D2D1::BitmapBrushProperties(extend_mode_x, extend_mode_y), |
| D2D1::BrushProperties(), |
| &brush); |
| return brush; |
| } |
| |
| // A platform wrapper for a Direct2D brush that makes sure the underlying |
| // ID2D1Brush COM object is released when this object is destroyed. |
| class Direct2DBrush : public gfx::Brush { |
| public: |
| explicit Direct2DBrush(ID2D1Brush* brush) : brush_(brush) { |
| } |
| |
| ID2D1Brush* brush() const { return brush_.get(); } |
| |
| private: |
| ScopedComPtr<ID2D1Brush> brush_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Direct2DBrush); |
| }; |
| |
| |
| } // namespace |
| |
| namespace gfx { |
| |
| // static |
| ID2D1Factory* CanvasDirect2D::d2d1_factory_ = NULL; |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // CanvasDirect2D, public: |
| |
| CanvasDirect2D::CanvasDirect2D(ID2D1RenderTarget* rt) : rt_(rt) { |
| // A RenderState entry is pushed onto the stack to track the clip count prior |
| // to any calls to Save*(). |
| state_.push(RenderState()); |
| rt_->BeginDraw(); |
| } |
| |
| CanvasDirect2D::~CanvasDirect2D() { |
| // Unwind any clips that were pushed outside of any Save*()/Restore() pairs. |
| int clip_count = state_.top().clip_count; |
| for (int i = 0; i < clip_count; ++i) |
| rt_->PopAxisAlignedClip(); |
| rt_->EndDraw(); |
| } |
| |
| // static |
| ID2D1Factory* CanvasDirect2D::GetD2D1Factory() { |
| if (!d2d1_factory_) |
| D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &d2d1_factory_); |
| return d2d1_factory_; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // CanvasDirect2D, Canvas implementation: |
| |
| void CanvasDirect2D::Save() { |
| SaveInternal(NULL); |
| } |
| |
| void CanvasDirect2D::SaveLayerAlpha(uint8 alpha) { |
| SaveLayerAlpha(alpha, gfx::Rect()); |
| } |
| |
| void CanvasDirect2D::SaveLayerAlpha(uint8 alpha, |
| const gfx::Rect& layer_bounds) { |
| D2D1_RECT_F bounds = D2D1::InfiniteRect(); |
| if (!layer_bounds.IsEmpty()) |
| bounds = RectToRectF(layer_bounds); |
| ID2D1Layer* layer = NULL; |
| HRESULT hr = rt_->CreateLayer(NULL, &layer); |
| if (SUCCEEDED(hr)) { |
| rt_->PushLayer(D2D1::LayerParameters(bounds, |
| NULL, |
| D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, |
| D2D1::IdentityMatrix(), |
| static_cast<float>(alpha) / 0xFF, |
| NULL, |
| D2D1_LAYER_OPTIONS_NONE), |
| layer); |
| } |
| SaveInternal(layer); |
| } |
| |
| void CanvasDirect2D::Restore() { |
| ID2D1Layer* layer = state_.top().layer; |
| if (layer) { |
| rt_->PopLayer(); |
| layer->Release(); |
| } |
| |
| int clip_count = state_.top().clip_count; |
| for (int i = 0; i < clip_count; ++i) |
| rt_->PopAxisAlignedClip(); |
| |
| state_.pop(); |
| // The state_ stack should never be empty - we should always have at least one |
| // entry to hold a clip count when there is no active save/restore entry. |
| CHECK(!state_.empty()) << "Called Restore() once too often!"; |
| |
| rt_->RestoreDrawingState(drawing_state_block_); |
| } |
| |
| bool CanvasDirect2D::ClipRectInt(int x, int y, int w, int h) { |
| rt_->PushAxisAlignedClip(RectToRectF(x, y, w, h), |
| D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); |
| // Increment the clip count so the call to PushAxisAlignedClip() can be |
| // balanced with a call to PopAxisAlignedClip in the next Restore(). |
| ++state_.top().clip_count; |
| return w > 0 && h > 0; |
| } |
| |
| void CanvasDirect2D::TranslateInt(int x, int y) { |
| D2D1_MATRIX_3X2_F raw; |
| rt_->GetTransform(&raw); |
| D2D1::Matrix3x2F transform(raw._11, raw._12, raw._21, raw._22, raw._31, |
| raw._32); |
| transform = D2D1::Matrix3x2F::Translation(static_cast<float>(x), |
| static_cast<float>(y)) * transform; |
| rt_->SetTransform(transform); |
| } |
| |
| void CanvasDirect2D::ScaleInt(int x, int y) { |
| D2D1_MATRIX_3X2_F raw; |
| rt_->GetTransform(&raw); |
| D2D1::Matrix3x2F transform(raw._11, raw._12, raw._21, raw._22, raw._31, |
| raw._32); |
| transform = D2D1::Matrix3x2F::Scale(static_cast<float>(x), |
| static_cast<float>(y)) * transform; |
| rt_->SetTransform(transform); |
| } |
| |
| void CanvasDirect2D::FillRectInt(const SkColor& color, |
| int x, int y, int w, int h) { |
| ScopedComPtr<ID2D1SolidColorBrush> solid_brush; |
| rt_->CreateSolidColorBrush(SkColorToColorF(color), solid_brush.Receive()); |
| rt_->FillRectangle(RectToRectF(x, y, w, h), solid_brush); |
| } |
| |
| void CanvasDirect2D::FillRectInt(const gfx::Brush* brush, |
| int x, int y, int w, int h) { |
| const Direct2DBrush* d2d_brush = static_cast<const Direct2DBrush*>(brush); |
| rt_->FillRectangle(RectToRectF(x, y, w, h), d2d_brush->brush()); |
| } |
| |
| void CanvasDirect2D::DrawRectInt(const SkColor& color, |
| int x, int y, int w, int h) { |
| ScopedComPtr<ID2D1SolidColorBrush> solid_brush; |
| rt_->CreateSolidColorBrush(SkColorToColorF(color), solid_brush.Receive()); |
| rt_->DrawRectangle(RectToRectF(x, y, w, h), solid_brush); |
| } |
| |
| void CanvasDirect2D::DrawRectInt(const SkColor& color, |
| int x, int y, int w, int h, |
| SkXfermode::Mode mode) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void CanvasDirect2D::DrawRectInt(int x, int y, int w, int h, |
| const SkPaint& paint) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void CanvasDirect2D::DrawLineInt(const SkColor& color, |
| int x1, int y1, |
| int x2, int y2) { |
| ScopedComPtr<ID2D1SolidColorBrush> solid_brush; |
| rt_->CreateSolidColorBrush(SkColorToColorF(color), solid_brush.Receive()); |
| rt_->DrawLine(PointToPoint2F(x1, y1), PointToPoint2F(x2, y2), solid_brush); |
| } |
| |
| void CanvasDirect2D::DrawBitmapInt(const SkBitmap& bitmap, int x, int y) { |
| ScopedComPtr<ID2D1Bitmap> d2d1_bitmap( |
| CreateD2D1BitmapFromSkBitmap(rt_, bitmap)); |
| rt_->DrawBitmap(d2d1_bitmap, |
| RectToRectF(x, y, bitmap.width(), bitmap.height()), |
| 1.0f, |
| D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, |
| RectToRectF(0, 0, bitmap.width(), bitmap.height())); |
| } |
| |
| void CanvasDirect2D::DrawBitmapInt(const SkBitmap& bitmap, |
| int x, int y, |
| const SkPaint& paint) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void CanvasDirect2D::DrawBitmapInt(const SkBitmap& bitmap, |
| int src_x, int src_y, int src_w, int src_h, |
| int dest_x, int dest_y, |
| int dest_w, int dest_h, |
| bool filter) { |
| ScopedComPtr<ID2D1Bitmap> d2d1_bitmap( |
| CreateD2D1BitmapFromSkBitmap(rt_, bitmap)); |
| rt_->DrawBitmap(d2d1_bitmap, |
| RectToRectF(dest_x, dest_y, dest_w, dest_h), |
| 1.0f, |
| FilterToInterpolationMode(filter), |
| RectToRectF(src_x, src_y, src_w, src_h)); |
| } |
| |
| void CanvasDirect2D::DrawBitmapInt(const SkBitmap& bitmap, |
| int src_x, int src_y, int src_w, int src_h, |
| int dest_x, int dest_y, |
| int dest_w, int dest_h, |
| bool filter, |
| const SkPaint& paint) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void CanvasDirect2D::DrawStringInt(const std::wstring& text, |
| const gfx::Font& font, |
| const SkColor& color, |
| int x, int y, int w, int h) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void CanvasDirect2D::DrawStringInt(const std::wstring& text, |
| const gfx::Font& font, |
| const SkColor& color, |
| const gfx::Rect& display_rect) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void CanvasDirect2D::DrawStringInt(const std::wstring& text, |
| const gfx::Font& font, |
| const SkColor& color, |
| int x, int y, int w, int h, |
| int flags) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void CanvasDirect2D::DrawFocusRect(int x, int y, int width, int height) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void CanvasDirect2D::TileImageInt(const SkBitmap& bitmap, |
| int x, int y, int w, int h) { |
| ScopedComPtr<ID2D1Brush> brush( |
| CreateD2D1BrushFromSkBitmap(rt_, bitmap, D2D1_EXTEND_MODE_WRAP, |
| D2D1_EXTEND_MODE_WRAP)); |
| rt_->FillRectangle(RectToRectF(x, y, w, h), brush); |
| } |
| |
| void CanvasDirect2D::TileImageInt(const SkBitmap& bitmap, |
| int src_x, int src_y, |
| int dest_x, int dest_y, int w, int h) { |
| NOTIMPLEMENTED(); |
| } |
| |
| gfx::NativeDrawingContext CanvasDirect2D::BeginPlatformPaint() { |
| DCHECK(!interop_rt_.get()); |
| interop_rt_.QueryFrom(rt_); |
| HDC dc = NULL; |
| if (interop_rt_.get()) |
| interop_rt_->GetDC(D2D1_DC_INITIALIZE_MODE_COPY, &dc); |
| return dc; |
| } |
| |
| void CanvasDirect2D::EndPlatformPaint() { |
| DCHECK(interop_rt_.get()); |
| interop_rt_->ReleaseDC(NULL); |
| interop_rt_.release(); |
| } |
| |
| CanvasSkia* CanvasDirect2D::AsCanvasSkia() { |
| return NULL; |
| } |
| |
| const CanvasSkia* CanvasDirect2D::AsCanvasSkia() const { |
| return NULL; |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // CanvasDirect2D, private: |
| |
| void CanvasDirect2D::SaveInternal(ID2D1Layer* layer) { |
| if (!drawing_state_block_) |
| GetD2D1Factory()->CreateDrawingStateBlock(drawing_state_block_.Receive()); |
| rt_->SaveDrawingState(drawing_state_block_.get()); |
| state_.push(RenderState(layer)); |
| } |
| |
| } // namespace gfx |