普通文本  |  238行  |  7.44 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "skia/ext/platform_device.h"

#include "skia/ext/skia_utils_win.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkPath.h"
#include "third_party/skia/include/core/SkRegion.h"
#include "third_party/skia/include/core/SkUtils.h"

namespace skia {

void InitializeDC(HDC context) {
  // Enables world transformation.
  // If the GM_ADVANCED graphics mode is set, GDI always draws arcs in the
  // counterclockwise direction in logical space. This is equivalent to the
  // statement that, in the GM_ADVANCED graphics mode, both arc control points
  // and arcs themselves fully respect the device context's world-to-device
  // transformation.
  BOOL res = SetGraphicsMode(context, GM_ADVANCED);
  SkASSERT(res != 0);

  // Enables dithering.
  res = SetStretchBltMode(context, HALFTONE);
  SkASSERT(res != 0);
  // As per SetStretchBltMode() documentation, SetBrushOrgEx() must be called
  // right after.
  res = SetBrushOrgEx(context, 0, 0, NULL);
  SkASSERT(res != 0);

  // Sets up default orientation.
  res = SetArcDirection(context, AD_CLOCKWISE);
  SkASSERT(res != 0);

  // Sets up default colors.
  res = SetBkColor(context, RGB(255, 255, 255));
  SkASSERT(res != CLR_INVALID);
  res = SetTextColor(context, RGB(0, 0, 0));
  SkASSERT(res != CLR_INVALID);
  res = SetDCBrushColor(context, RGB(255, 255, 255));
  SkASSERT(res != CLR_INVALID);
  res = SetDCPenColor(context, RGB(0, 0, 0));
  SkASSERT(res != CLR_INVALID);

  // Sets up default transparency.
  res = SetBkMode(context, OPAQUE);
  SkASSERT(res != 0);
  res = SetROP2(context, R2_COPYPEN);
  SkASSERT(res != 0);
}

PlatformSurface PlatformDevice::BeginPlatformPaint() {
  return 0;
}

void PlatformDevice::EndPlatformPaint() {
  // We don't clear the DC here since it will be likely to be used again.
  // Flushing will be done in onAccessBitmap.
}

void PlatformDevice::DrawToNativeContext(PlatformSurface surface, int x, int y,
                                         const PlatformRect* src_rect) {
}

// static
bool PlatformDevice::LoadPathToDC(HDC context, const SkPath& path) {
  switch (path.getFillType()) {
    case SkPath::kWinding_FillType: {
      int res = SetPolyFillMode(context, WINDING);
      SkASSERT(res != 0);
      break;
    }
    case SkPath::kEvenOdd_FillType: {
      int res = SetPolyFillMode(context, ALTERNATE);
      SkASSERT(res != 0);
      break;
    }
    default: {
      SkASSERT(false);
      break;
    }
  }
  BOOL res = BeginPath(context);
  if (!res) {
      return false;
  }

  CubicPaths paths;
  if (!SkPathToCubicPaths(&paths, path))
    return false;

  std::vector<POINT> points;
  for (CubicPaths::const_iterator path(paths.begin()); path != paths.end();
       ++path) {
    if (!path->size())
      continue;
    points.resize(0);
    points.reserve(path->size() * 3 / 4 + 1);
    points.push_back(SkPointToPOINT(path->front().p[0]));
    for (CubicPath::const_iterator point(path->begin()); point != path->end();
       ++point) {
      // Never add point->p[0]
      points.push_back(SkPointToPOINT(point->p[1]));
      points.push_back(SkPointToPOINT(point->p[2]));
      points.push_back(SkPointToPOINT(point->p[3]));
    }
    SkASSERT((points.size() - 1) % 3 == 0);
    // This is slightly inefficient since all straight line and quadratic lines
    // are "upgraded" to a cubic line.
    // TODO(maruel):  http://b/1147346 We should use
    // PolyDraw/PolyBezier/Polyline whenever possible.
    res = PolyBezier(context, &points.front(),
                     static_cast<DWORD>(points.size()));
    SkASSERT(res != 0);
    if (res == 0)
      break;
  }
  if (res == 0) {
    // Make sure the path is discarded.
    AbortPath(context);
  } else {
    res = EndPath(context);
    SkASSERT(res != 0);
  }
  return true;
}

// static
void PlatformDevice::LoadTransformToDC(HDC dc, const SkMatrix& matrix) {
  XFORM xf;
  xf.eM11 = matrix[SkMatrix::kMScaleX];
  xf.eM21 = matrix[SkMatrix::kMSkewX];
  xf.eDx = matrix[SkMatrix::kMTransX];
  xf.eM12 = matrix[SkMatrix::kMSkewY];
  xf.eM22 = matrix[SkMatrix::kMScaleY];
  xf.eDy = matrix[SkMatrix::kMTransY];
  SetWorldTransform(dc, &xf);
}

// static
bool PlatformDevice::SkPathToCubicPaths(CubicPaths* paths,
                                        const SkPath& skpath) {
  paths->clear();
  CubicPath* current_path = NULL;
  SkPoint current_points[4];
  CubicPoints points_to_add;
  SkPath::Iter iter(skpath, false);
  for (SkPath::Verb verb = iter.next(current_points);
       verb != SkPath::kDone_Verb;
       verb = iter.next(current_points)) {
    switch (verb) {
      case SkPath::kMove_Verb: {  // iter.next returns 1 point
        // Ignores it since the point is copied in the next operation. See
        // SkPath::Iter::next() for reference.
        paths->push_back(CubicPath());
        current_path = &paths->back();
        // Skip point addition.
        continue;
      }
      case SkPath::kLine_Verb: {  // iter.next returns 2 points
        points_to_add.p[0] = current_points[0];
        points_to_add.p[1] = current_points[0];
        points_to_add.p[2] = current_points[1];
        points_to_add.p[3] = current_points[1];
        break;
      }
      case SkPath::kQuad_Verb: {  // iter.next returns 3 points
        points_to_add.p[0] = current_points[0];
        points_to_add.p[1] = current_points[1];
        points_to_add.p[2] = current_points[2];
        points_to_add.p[3] = current_points[2];
        break;
      }
      case SkPath::kCubic_Verb: {  // iter.next returns 4 points
        points_to_add.p[0] = current_points[0];
        points_to_add.p[1] = current_points[1];
        points_to_add.p[2] = current_points[2];
        points_to_add.p[3] = current_points[3];
        break;
      }
      case SkPath::kClose_Verb: {  // iter.next returns 1 point (the last point)
        paths->push_back(CubicPath());
        current_path = &paths->back();
        continue;
      }
      default: {
        current_path = NULL;
        // Will return false.
        break;
      }
    }
    SkASSERT(current_path);
    if (!current_path) {
      paths->clear();
      return false;
    }
    current_path->push_back(points_to_add);
  }
  return true;
}

// static
void PlatformDevice::LoadClippingRegionToDC(HDC context,
                                            const SkRegion& region,
                                            const SkMatrix& transformation) {
  HRGN hrgn;
  if (region.isEmpty()) {
    // region can be empty, in which case everything will be clipped.
    hrgn = CreateRectRgn(0, 0, 0, 0);
  } else if (region.isRect()) {
    // We don't apply transformation, because the translation is already applied
    // to the region.
    hrgn = CreateRectRgnIndirect(&SkIRectToRECT(region.getBounds()));
  } else {
    // It is complex.
    SkPath path;
    region.getBoundaryPath(&path);
    // Clip. Note that windows clipping regions are not affected by the
    // transform so apply it manually.
    // Since the transform is given as the original translation of canvas, we
    // should apply it in reverse.
    SkMatrix t(transformation);
    t.setTranslateX(-t.getTranslateX());
    t.setTranslateY(-t.getTranslateY());
    path.transform(t);
    LoadPathToDC(context, path);
    hrgn = PathToRegion(context);
  }
  int result = SelectClipRgn(context, hrgn);
  SkASSERT(result != ERROR);
  result = DeleteObject(hrgn);
  SkASSERT(result != 0);
}

}  // namespace skia