| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "skia/ext/vector_platform_device_emf_win.h" | |
| 6 | |
| 7 #include <windows.h> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "base/strings/string16.h" | |
| 11 #include "skia/ext/bitmap_platform_device.h" | |
| 12 #include "skia/ext/skia_utils_win.h" | |
| 13 #include "third_party/skia/include/core/SkPathEffect.h" | |
| 14 #include "third_party/skia/include/core/SkTemplates.h" | |
| 15 #include "third_party/skia/include/core/SkUtils.h" | |
| 16 #include "third_party/skia/include/ports/SkTypeface_win.h" | |
| 17 | |
| 18 namespace skia { | |
| 19 | |
| 20 #define CHECK_FOR_NODRAW_ANNOTATION(paint) \ | |
| 21 do { if (paint.isNoDrawAnnotation()) { return; } } while (0) | |
| 22 | |
| 23 // static | |
| 24 SkBaseDevice* VectorPlatformDeviceEmf::CreateDevice( | |
| 25 int width, int height, bool is_opaque, HANDLE shared_section) { | |
| 26 if (!is_opaque) { | |
| 27 // TODO(maruel): http://crbug.com/18382 When restoring a semi-transparent | |
| 28 // layer, i.e. merging it, we need to rasterize it because GDI doesn't | |
| 29 // support transparency except for AlphaBlend(). Right now, a | |
| 30 // BitmapPlatformDevice is created when VectorCanvas think a saveLayers() | |
| 31 // call is being done. The way to save a layer would be to create an | |
| 32 // EMF-based VectorDevice and have this device registers the drawing. When | |
| 33 // playing back the device into a bitmap, do it at the printer's dpi instead | |
| 34 // of the layout's dpi (which is much lower). | |
| 35 return BitmapPlatformDevice::Create(width, height, is_opaque, | |
| 36 shared_section); | |
| 37 } | |
| 38 | |
| 39 // TODO(maruel): http://crbug.com/18383 Look if it would be worth to | |
| 40 // increase the resolution by ~10x (any worthy factor) to increase the | |
| 41 // rendering precision (think about printing) while using a relatively | |
| 42 // low dpi. This happens because we receive float as input but the GDI | |
| 43 // functions works with integers. The idea is to premultiply the matrix | |
| 44 // with this factor and multiply each SkScalar that are passed to | |
| 45 // SkScalarRound(value) as SkScalarRound(value * 10). Safari is already | |
| 46 // doing the same for text rendering. | |
| 47 SkASSERT(shared_section); | |
| 48 SkBaseDevice* device = VectorPlatformDeviceEmf::create( | |
| 49 reinterpret_cast<HDC>(shared_section), width, height); | |
| 50 return device; | |
| 51 } | |
| 52 | |
| 53 static void FillBitmapInfoHeader(int width, int height, BITMAPINFOHEADER* hdr) { | |
| 54 hdr->biSize = sizeof(BITMAPINFOHEADER); | |
| 55 hdr->biWidth = width; | |
| 56 hdr->biHeight = -height; // Minus means top-down bitmap. | |
| 57 hdr->biPlanes = 1; | |
| 58 hdr->biBitCount = 32; | |
| 59 hdr->biCompression = BI_RGB; // no compression | |
| 60 hdr->biSizeImage = 0; | |
| 61 hdr->biXPelsPerMeter = 1; | |
| 62 hdr->biYPelsPerMeter = 1; | |
| 63 hdr->biClrUsed = 0; | |
| 64 hdr->biClrImportant = 0; | |
| 65 } | |
| 66 | |
| 67 SkBaseDevice* VectorPlatformDeviceEmf::create(HDC dc, int width, int height) { | |
| 68 InitializeDC(dc); | |
| 69 | |
| 70 // Link the SkBitmap to the current selected bitmap in the device context. | |
| 71 SkBitmap bitmap; | |
| 72 HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP); | |
| 73 bool succeeded = false; | |
| 74 if (selected_bitmap != NULL) { | |
| 75 BITMAP bitmap_data = {0}; | |
| 76 if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) == | |
| 77 sizeof(BITMAP)) { | |
| 78 // The context has a bitmap attached. Attach our SkBitmap to it. | |
| 79 // Warning: If the bitmap gets unselected from the HDC, | |
| 80 // VectorPlatformDeviceEmf has no way to detect this, so the HBITMAP | |
| 81 // could be released while SkBitmap still has a reference to it. Be | |
| 82 // cautious. | |
| 83 if (width == bitmap_data.bmWidth && height == bitmap_data.bmHeight) { | |
| 84 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); | |
| 85 succeeded = bitmap.installPixels(info, bitmap_data.bmBits, | |
| 86 bitmap_data.bmWidthBytes); | |
| 87 } | |
| 88 } | |
| 89 } | |
| 90 | |
| 91 if (!succeeded) | |
| 92 bitmap.setInfo(SkImageInfo::MakeUnknown(width, height)); | |
| 93 | |
| 94 return new VectorPlatformDeviceEmf(dc, bitmap); | |
| 95 } | |
| 96 | |
| 97 VectorPlatformDeviceEmf::VectorPlatformDeviceEmf(HDC dc, const SkBitmap& bitmap) | |
| 98 : SkBitmapDevice(bitmap), | |
| 99 hdc_(dc), | |
| 100 previous_brush_(NULL), | |
| 101 previous_pen_(NULL) { | |
| 102 transform_.reset(); | |
| 103 SetPlatformDevice(this, this); | |
| 104 } | |
| 105 | |
| 106 VectorPlatformDeviceEmf::~VectorPlatformDeviceEmf() { | |
| 107 SkASSERT(previous_brush_ == NULL); | |
| 108 SkASSERT(previous_pen_ == NULL); | |
| 109 } | |
| 110 | |
| 111 HDC VectorPlatformDeviceEmf::BeginPlatformPaint() { | |
| 112 return hdc_; | |
| 113 } | |
| 114 | |
| 115 void VectorPlatformDeviceEmf::drawPaint(const SkDraw& draw, | |
| 116 const SkPaint& paint) { | |
| 117 // TODO(maruel): Bypass the current transformation matrix. | |
| 118 SkRect rect; | |
| 119 rect.fLeft = 0; | |
| 120 rect.fTop = 0; | |
| 121 rect.fRight = SkIntToScalar(width() + 1); | |
| 122 rect.fBottom = SkIntToScalar(height() + 1); | |
| 123 drawRect(draw, rect, paint); | |
| 124 } | |
| 125 | |
| 126 void VectorPlatformDeviceEmf::drawPoints(const SkDraw& draw, | |
| 127 SkCanvas::PointMode mode, | |
| 128 size_t count, | |
| 129 const SkPoint pts[], | |
| 130 const SkPaint& paint) { | |
| 131 if (!count) | |
| 132 return; | |
| 133 | |
| 134 if (mode == SkCanvas::kPoints_PointMode) { | |
| 135 SkASSERT(false); | |
| 136 return; | |
| 137 } | |
| 138 | |
| 139 SkPaint tmp_paint(paint); | |
| 140 tmp_paint.setStyle(SkPaint::kStroke_Style); | |
| 141 | |
| 142 // Draw a path instead. | |
| 143 SkPath path; | |
| 144 switch (mode) { | |
| 145 case SkCanvas::kLines_PointMode: | |
| 146 if (count % 2) { | |
| 147 SkASSERT(false); | |
| 148 return; | |
| 149 } | |
| 150 for (size_t i = 0; i < count / 2; ++i) { | |
| 151 path.moveTo(pts[2 * i]); | |
| 152 path.lineTo(pts[2 * i + 1]); | |
| 153 } | |
| 154 break; | |
| 155 case SkCanvas::kPolygon_PointMode: | |
| 156 path.moveTo(pts[0]); | |
| 157 for (size_t i = 1; i < count; ++i) { | |
| 158 path.lineTo(pts[i]); | |
| 159 } | |
| 160 break; | |
| 161 default: | |
| 162 SkASSERT(false); | |
| 163 return; | |
| 164 } | |
| 165 // Draw the calculated path. | |
| 166 drawPath(draw, path, tmp_paint); | |
| 167 } | |
| 168 | |
| 169 void VectorPlatformDeviceEmf::drawRect(const SkDraw& draw, | |
| 170 const SkRect& rect, | |
| 171 const SkPaint& paint) { | |
| 172 CHECK_FOR_NODRAW_ANNOTATION(paint); | |
| 173 if (paint.getPathEffect()) { | |
| 174 // Draw a path instead. | |
| 175 SkPath path_orginal; | |
| 176 path_orginal.addRect(rect); | |
| 177 | |
| 178 // Apply the path effect to the rect. | |
| 179 SkPath path_modified; | |
| 180 paint.getFillPath(path_orginal, &path_modified); | |
| 181 | |
| 182 // Removes the path effect from the temporary SkPaint object. | |
| 183 SkPaint paint_no_effet(paint); | |
| 184 paint_no_effet.setPathEffect(NULL); | |
| 185 | |
| 186 // Draw the calculated path. | |
| 187 drawPath(draw, path_modified, paint_no_effet); | |
| 188 return; | |
| 189 } | |
| 190 | |
| 191 if (!ApplyPaint(paint)) { | |
| 192 return; | |
| 193 } | |
| 194 HDC dc = BeginPlatformPaint(); | |
| 195 if (!Rectangle(dc, SkScalarRoundToInt(rect.fLeft), | |
| 196 SkScalarRoundToInt(rect.fTop), | |
| 197 SkScalarRoundToInt(rect.fRight), | |
| 198 SkScalarRoundToInt(rect.fBottom))) { | |
| 199 SkASSERT(false); | |
| 200 } | |
| 201 EndPlatformPaint(); | |
| 202 Cleanup(); | |
| 203 } | |
| 204 | |
| 205 void VectorPlatformDeviceEmf::drawRRect(const SkDraw& draw, const SkRRect& rr, | |
| 206 const SkPaint& paint) { | |
| 207 SkPath path; | |
| 208 path.addRRect(rr); | |
| 209 this->drawPath(draw, path, paint, NULL, true); | |
| 210 } | |
| 211 | |
| 212 void VectorPlatformDeviceEmf::drawPath(const SkDraw& draw, | |
| 213 const SkPath& path, | |
| 214 const SkPaint& paint, | |
| 215 const SkMatrix* prePathMatrix, | |
| 216 bool pathIsMutable) { | |
| 217 CHECK_FOR_NODRAW_ANNOTATION(paint); | |
| 218 if (paint.getPathEffect()) { | |
| 219 // Apply the path effect forehand. | |
| 220 SkPath path_modified; | |
| 221 paint.getFillPath(path, &path_modified); | |
| 222 | |
| 223 // Removes the path effect from the temporary SkPaint object. | |
| 224 SkPaint paint_no_effet(paint); | |
| 225 paint_no_effet.setPathEffect(NULL); | |
| 226 | |
| 227 // Draw the calculated path. | |
| 228 drawPath(draw, path_modified, paint_no_effet); | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 if (!ApplyPaint(paint)) { | |
| 233 return; | |
| 234 } | |
| 235 HDC dc = BeginPlatformPaint(); | |
| 236 if (PlatformDevice::LoadPathToDC(dc, path)) { | |
| 237 switch (paint.getStyle()) { | |
| 238 case SkPaint::kFill_Style: { | |
| 239 BOOL res = StrokeAndFillPath(dc); | |
| 240 SkASSERT(res != 0); | |
| 241 break; | |
| 242 } | |
| 243 case SkPaint::kStroke_Style: { | |
| 244 BOOL res = StrokePath(dc); | |
| 245 SkASSERT(res != 0); | |
| 246 break; | |
| 247 } | |
| 248 case SkPaint::kStrokeAndFill_Style: { | |
| 249 BOOL res = StrokeAndFillPath(dc); | |
| 250 SkASSERT(res != 0); | |
| 251 break; | |
| 252 } | |
| 253 default: | |
| 254 SkASSERT(false); | |
| 255 break; | |
| 256 } | |
| 257 } | |
| 258 EndPlatformPaint(); | |
| 259 Cleanup(); | |
| 260 } | |
| 261 | |
| 262 void VectorPlatformDeviceEmf::drawBitmapRect(const SkDraw& draw, | |
| 263 const SkBitmap& bitmap, | |
| 264 const SkRect* src, | |
| 265 const SkRect& dst, | |
| 266 const SkPaint& paint, | |
| 267 SkCanvas::DrawBitmapRectFlags flags
) { | |
| 268 SkMatrix matrix; | |
| 269 SkRect bitmapBounds, tmpSrc, tmpDst; | |
| 270 SkBitmap tmpBitmap; | |
| 271 | |
| 272 bitmapBounds.isetWH(bitmap.width(), bitmap.height()); | |
| 273 | |
| 274 // Compute matrix from the two rectangles | |
| 275 if (src) { | |
| 276 tmpSrc = *src; | |
| 277 } else { | |
| 278 tmpSrc = bitmapBounds; | |
| 279 } | |
| 280 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); | |
| 281 | |
| 282 const SkBitmap* bitmapPtr = &bitmap; | |
| 283 | |
| 284 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if | |
| 285 // needed (if the src was clipped). No check needed if src==null. | |
| 286 if (src) { | |
| 287 if (!bitmapBounds.contains(*src)) { | |
| 288 if (!tmpSrc.intersect(bitmapBounds)) { | |
| 289 return; // nothing to draw | |
| 290 } | |
| 291 // recompute dst, based on the smaller tmpSrc | |
| 292 matrix.mapRect(&tmpDst, tmpSrc); | |
| 293 } | |
| 294 | |
| 295 // since we may need to clamp to the borders of the src rect within | |
| 296 // the bitmap, we extract a subset. | |
| 297 // TODO: make sure this is handled in drawrect and remove it from here. | |
| 298 SkIRect srcIR; | |
| 299 tmpSrc.roundOut(&srcIR); | |
| 300 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { | |
| 301 return; | |
| 302 } | |
| 303 bitmapPtr = &tmpBitmap; | |
| 304 | |
| 305 // Since we did an extract, we need to adjust the matrix accordingly | |
| 306 SkScalar dx = 0, dy = 0; | |
| 307 if (srcIR.fLeft > 0) { | |
| 308 dx = SkIntToScalar(srcIR.fLeft); | |
| 309 } | |
| 310 if (srcIR.fTop > 0) { | |
| 311 dy = SkIntToScalar(srcIR.fTop); | |
| 312 } | |
| 313 if (dx || dy) { | |
| 314 matrix.preTranslate(dx, dy); | |
| 315 } | |
| 316 } | |
| 317 this->drawBitmap(draw, *bitmapPtr, matrix, paint); | |
| 318 } | |
| 319 | |
| 320 void VectorPlatformDeviceEmf::drawBitmap(const SkDraw& draw, | |
| 321 const SkBitmap& bitmap, | |
| 322 const SkMatrix& matrix, | |
| 323 const SkPaint& paint) { | |
| 324 // Load the temporary matrix. This is what will translate, rotate and resize | |
| 325 // the bitmap. | |
| 326 SkMatrix actual_transform(transform_); | |
| 327 actual_transform.preConcat(matrix); | |
| 328 LoadTransformToDC(hdc_, actual_transform); | |
| 329 | |
| 330 InternalDrawBitmap(bitmap, 0, 0, paint); | |
| 331 | |
| 332 // Restore the original matrix. | |
| 333 LoadTransformToDC(hdc_, transform_); | |
| 334 } | |
| 335 | |
| 336 void VectorPlatformDeviceEmf::drawSprite(const SkDraw& draw, | |
| 337 const SkBitmap& bitmap, | |
| 338 int x, int y, | |
| 339 const SkPaint& paint) { | |
| 340 SkMatrix identity; | |
| 341 identity.reset(); | |
| 342 LoadTransformToDC(hdc_, identity); | |
| 343 | |
| 344 InternalDrawBitmap(bitmap, x, y, paint); | |
| 345 | |
| 346 // Restore the original matrix. | |
| 347 LoadTransformToDC(hdc_, transform_); | |
| 348 } | |
| 349 | |
| 350 ///////////////////////////////////////////////////////////////////////// | |
| 351 | |
| 352 static bool gdiCanHandleText(const SkPaint& paint) { | |
| 353 return !paint.getShader() && | |
| 354 !paint.getPathEffect() && | |
| 355 (SkPaint::kFill_Style == paint.getStyle()) && | |
| 356 (255 == paint.getAlpha()); | |
| 357 } | |
| 358 | |
| 359 class SkGDIFontSetup { | |
| 360 public: | |
| 361 SkGDIFontSetup() : | |
| 362 fHDC(NULL), | |
| 363 fNewFont(NULL), | |
| 364 fSavedFont(NULL), | |
| 365 fSavedTextColor(0), | |
| 366 fUseGDI(false) { | |
| 367 SkDEBUGCODE(fUseGDIHasBeenCalled = false;) | |
| 368 } | |
| 369 ~SkGDIFontSetup(); | |
| 370 | |
| 371 // can only be called once | |
| 372 bool useGDI(HDC hdc, const SkPaint&); | |
| 373 | |
| 374 private: | |
| 375 HDC fHDC; | |
| 376 HFONT fNewFont; | |
| 377 HFONT fSavedFont; | |
| 378 COLORREF fSavedTextColor; | |
| 379 bool fUseGDI; | |
| 380 SkDEBUGCODE(bool fUseGDIHasBeenCalled;) | |
| 381 }; | |
| 382 | |
| 383 bool SkGDIFontSetup::useGDI(HDC hdc, const SkPaint& paint) { | |
| 384 SkASSERT(!fUseGDIHasBeenCalled); | |
| 385 SkDEBUGCODE(fUseGDIHasBeenCalled = true;) | |
| 386 | |
| 387 fUseGDI = gdiCanHandleText(paint); | |
| 388 if (fUseGDI) { | |
| 389 fSavedTextColor = GetTextColor(hdc); | |
| 390 SetTextColor(hdc, skia::SkColorToCOLORREF(paint.getColor())); | |
| 391 | |
| 392 LOGFONT lf = {0}; | |
| 393 SkLOGFONTFromTypeface(paint.getTypeface(), &lf); | |
| 394 lf.lfHeight = -SkScalarRoundToInt(paint.getTextSize()); | |
| 395 fNewFont = CreateFontIndirect(&lf); | |
| 396 fSavedFont = (HFONT)::SelectObject(hdc, fNewFont); | |
| 397 fHDC = hdc; | |
| 398 } | |
| 399 return fUseGDI; | |
| 400 } | |
| 401 | |
| 402 SkGDIFontSetup::~SkGDIFontSetup() { | |
| 403 if (fUseGDI) { | |
| 404 ::SelectObject(fHDC, fSavedFont); | |
| 405 ::DeleteObject(fNewFont); | |
| 406 SetTextColor(fHDC, fSavedTextColor); | |
| 407 } | |
| 408 } | |
| 409 | |
| 410 static SkScalar getAscent(const SkPaint& paint) { | |
| 411 SkPaint::FontMetrics fm; | |
| 412 paint.getFontMetrics(&fm); | |
| 413 return fm.fAscent; | |
| 414 } | |
| 415 | |
| 416 // return the options int for ExtTextOut. Only valid if the paint's text | |
| 417 // encoding is not UTF8 (in which case ExtTextOut can't be used). | |
| 418 static UINT getTextOutOptions(const SkPaint& paint) { | |
| 419 if (SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()) { | |
| 420 return ETO_GLYPH_INDEX; | |
| 421 } else { | |
| 422 SkASSERT(SkPaint::kUTF16_TextEncoding == paint.getTextEncoding()); | |
| 423 return 0; | |
| 424 } | |
| 425 } | |
| 426 | |
| 427 static SkiaEnsureTypefaceCharactersAccessible | |
| 428 g_skia_ensure_typeface_characters_accessible = NULL; | |
| 429 | |
| 430 SK_API void SetSkiaEnsureTypefaceCharactersAccessible( | |
| 431 SkiaEnsureTypefaceCharactersAccessible func) { | |
| 432 // This function is supposed to be called once in process life time. | |
| 433 SkASSERT(g_skia_ensure_typeface_characters_accessible == NULL); | |
| 434 g_skia_ensure_typeface_characters_accessible = func; | |
| 435 } | |
| 436 | |
| 437 void EnsureTypefaceCharactersAccessible( | |
| 438 const SkTypeface& typeface, const wchar_t* text, unsigned int text_length) { | |
| 439 LOGFONT lf = {0}; | |
| 440 SkLOGFONTFromTypeface(&typeface, &lf); | |
| 441 g_skia_ensure_typeface_characters_accessible(lf, text, text_length); | |
| 442 } | |
| 443 | |
| 444 bool EnsureExtTextOut(HDC hdc, int x, int y, UINT options, const RECT * lprect, | |
| 445 LPCWSTR text, unsigned int characters, const int * lpDx, | |
| 446 SkTypeface* const typeface) { | |
| 447 bool success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx); | |
| 448 if (!success) { | |
| 449 if (typeface) { | |
| 450 EnsureTypefaceCharactersAccessible(*typeface, | |
| 451 text, | |
| 452 characters); | |
| 453 success = ExtTextOut(hdc, x, y, options, lprect, text, characters, lpDx); | |
| 454 if (!success) { | |
| 455 LOGFONT lf = {0}; | |
| 456 SkLOGFONTFromTypeface(typeface, &lf); | |
| 457 VLOG(1) << "SkFontHost::EnsureTypefaceCharactersAccessible FAILED for " | |
| 458 << " FaceName = " << lf.lfFaceName | |
| 459 << " and characters: " << base::string16(text, characters); | |
| 460 } | |
| 461 } else { | |
| 462 VLOG(1) << "ExtTextOut FAILED for default FaceName " | |
| 463 << " and characters: " << base::string16(text, characters); | |
| 464 } | |
| 465 } | |
| 466 return success; | |
| 467 } | |
| 468 | |
| 469 void VectorPlatformDeviceEmf::drawText(const SkDraw& draw, | |
| 470 const void* text, | |
| 471 size_t byteLength, | |
| 472 SkScalar x, | |
| 473 SkScalar y, | |
| 474 const SkPaint& paint) { | |
| 475 SkGDIFontSetup setup; | |
| 476 bool useDrawPath = true; | |
| 477 | |
| 478 if (SkPaint::kUTF8_TextEncoding != paint.getTextEncoding() | |
| 479 && setup.useGDI(hdc_, paint)) { | |
| 480 UINT options = getTextOutOptions(paint); | |
| 481 UINT count = byteLength >> 1; | |
| 482 useDrawPath = !EnsureExtTextOut(hdc_, SkScalarRoundToInt(x), | |
| 483 SkScalarRoundToInt(y + getAscent(paint)), options, 0, | |
| 484 reinterpret_cast<const wchar_t*>(text), count, NULL, | |
| 485 paint.getTypeface()); | |
| 486 } | |
| 487 | |
| 488 if (useDrawPath) { | |
| 489 SkPath path; | |
| 490 paint.getTextPath(text, byteLength, x, y, &path); | |
| 491 drawPath(draw, path, paint); | |
| 492 } | |
| 493 } | |
| 494 | |
| 495 static size_t size_utf8(const char* text) { | |
| 496 return SkUTF8_CountUTF8Bytes(text); | |
| 497 } | |
| 498 | |
| 499 static size_t size_utf16(const char* text) { | |
| 500 uint16_t c = *reinterpret_cast<const uint16_t*>(text); | |
| 501 return SkUTF16_IsHighSurrogate(c) ? 4 : 2; | |
| 502 } | |
| 503 | |
| 504 static size_t size_glyphid(const char* text) { | |
| 505 return 2; | |
| 506 } | |
| 507 | |
| 508 void VectorPlatformDeviceEmf::drawPosText(const SkDraw& draw, | |
| 509 const void* text, | |
| 510 size_t len, | |
| 511 const SkScalar pos[], | |
| 512 int scalarsPerPos, | |
| 513 const SkPoint& offset, | |
| 514 const SkPaint& paint) { | |
| 515 SkGDIFontSetup setup; | |
| 516 bool useDrawText = true; | |
| 517 | |
| 518 if (scalarsPerPos == 2 && len >= 2 && | |
| 519 SkPaint::kUTF8_TextEncoding != paint.getTextEncoding() && | |
| 520 setup.useGDI(hdc_, paint)) { | |
| 521 int startX = SkScalarRoundToInt(pos[0] + offset.x()); | |
| 522 int startY = SkScalarRoundToInt(pos[1] + offset.y() + getAscent(paint)); | |
| 523 const int count = len >> 1; | |
| 524 SkAutoSTMalloc<64, INT> storage(count); | |
| 525 INT* advances = storage.get(); | |
| 526 for (int i = 0; i < count - 1; ++i) { | |
| 527 advances[i] = SkScalarRoundToInt(pos[2] - pos[0]); | |
| 528 pos += 2; | |
| 529 } | |
| 530 advances[count - 1] = 0; | |
| 531 useDrawText = !EnsureExtTextOut(hdc_, startX, startY, | |
| 532 getTextOutOptions(paint), 0, reinterpret_cast<const wchar_t*>(text), | |
| 533 count, advances, paint.getTypeface()); | |
| 534 } | |
| 535 | |
| 536 if (useDrawText) { | |
| 537 size_t (*bytesPerCodePoint)(const char*); | |
| 538 switch (paint.getTextEncoding()) { | |
| 539 case SkPaint::kUTF8_TextEncoding: | |
| 540 bytesPerCodePoint = size_utf8; | |
| 541 break; | |
| 542 case SkPaint::kUTF16_TextEncoding: | |
| 543 bytesPerCodePoint = size_utf16; | |
| 544 break; | |
| 545 default: | |
| 546 SkASSERT(SkPaint::kGlyphID_TextEncoding == paint.getTextEncoding()); | |
| 547 bytesPerCodePoint = size_glyphid; | |
| 548 break; | |
| 549 } | |
| 550 | |
| 551 const char* curr = reinterpret_cast<const char*>(text); | |
| 552 const char* stop = curr + len; | |
| 553 while (curr < stop) { | |
| 554 SkScalar x = offset.x() + pos[0]; | |
| 555 SkScalar y = offset.y() + (2 == scalarsPerPos ? pos[1] : 0); | |
| 556 | |
| 557 size_t bytes = bytesPerCodePoint(curr); | |
| 558 drawText(draw, curr, bytes, x, y, paint); | |
| 559 curr += bytes; | |
| 560 pos += scalarsPerPos; | |
| 561 } | |
| 562 } | |
| 563 } | |
| 564 | |
| 565 void VectorPlatformDeviceEmf::drawTextOnPath(const SkDraw& draw, | |
| 566 const void* text, | |
| 567 size_t len, | |
| 568 const SkPath& path, | |
| 569 const SkMatrix* matrix, | |
| 570 const SkPaint& paint) { | |
| 571 // This function isn't used in the code. Verify this assumption. | |
| 572 SkASSERT(false); | |
| 573 } | |
| 574 | |
| 575 void VectorPlatformDeviceEmf::drawVertices(const SkDraw& draw, | |
| 576 SkCanvas::VertexMode vmode, | |
| 577 int vertexCount, | |
| 578 const SkPoint vertices[], | |
| 579 const SkPoint texs[], | |
| 580 const SkColor colors[], | |
| 581 SkXfermode* xmode, | |
| 582 const uint16_t indices[], | |
| 583 int indexCount, | |
| 584 const SkPaint& paint) { | |
| 585 // This function isn't used in the code. Verify this assumption. | |
| 586 SkASSERT(false); | |
| 587 } | |
| 588 | |
| 589 void VectorPlatformDeviceEmf::drawDevice(const SkDraw& draw, | |
| 590 SkBaseDevice* device, | |
| 591 int x, | |
| 592 int y, | |
| 593 const SkPaint& paint) { | |
| 594 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if | |
| 595 // it is a vectorial device. | |
| 596 drawSprite(draw, device->accessBitmap(false), x, y, paint); | |
| 597 } | |
| 598 | |
| 599 bool VectorPlatformDeviceEmf::ApplyPaint(const SkPaint& paint) { | |
| 600 // Note: The goal here is to transfert the SkPaint's state to the HDC's state. | |
| 601 // This function does not execute the SkPaint drawing commands. These should | |
| 602 // be executed in drawPaint(). | |
| 603 | |
| 604 SkPaint::Style style = paint.getStyle(); | |
| 605 if (!paint.getAlpha()) | |
| 606 style = (SkPaint::Style) SkPaint::kStyleCount; | |
| 607 | |
| 608 switch (style) { | |
| 609 case SkPaint::kFill_Style: | |
| 610 if (!CreateBrush(true, paint) || | |
| 611 !CreatePen(false, paint)) | |
| 612 return false; | |
| 613 break; | |
| 614 case SkPaint::kStroke_Style: | |
| 615 if (!CreateBrush(false, paint) || | |
| 616 !CreatePen(true, paint)) | |
| 617 return false; | |
| 618 break; | |
| 619 case SkPaint::kStrokeAndFill_Style: | |
| 620 if (!CreateBrush(true, paint) || | |
| 621 !CreatePen(true, paint)) | |
| 622 return false; | |
| 623 break; | |
| 624 default: | |
| 625 if (!CreateBrush(false, paint) || | |
| 626 !CreatePen(false, paint)) | |
| 627 return false; | |
| 628 break; | |
| 629 } | |
| 630 | |
| 631 /* | |
| 632 getFlags(); | |
| 633 isAntiAlias(); | |
| 634 isDither() | |
| 635 isLinearText() | |
| 636 isSubpixelText() | |
| 637 isUnderlineText() | |
| 638 isStrikeThruText() | |
| 639 isFakeBoldText() | |
| 640 isDevKernText() | |
| 641 isFilterBitmap() | |
| 642 | |
| 643 // Skia's text is not used. This should be fixed. | |
| 644 getTextAlign() | |
| 645 getTextScaleX() | |
| 646 getTextSkewX() | |
| 647 getTextEncoding() | |
| 648 getFontMetrics() | |
| 649 getFontSpacing() | |
| 650 */ | |
| 651 | |
| 652 // BUG 1094907: Implement shaders. Shaders currently in use: | |
| 653 // SkShader::CreateBitmapShader | |
| 654 // SkGradientShader::CreateRadial | |
| 655 // SkGradientShader::CreateLinear | |
| 656 // SkASSERT(!paint.getShader()); | |
| 657 | |
| 658 // http://b/1106647 Implement loopers and mask filter. Looper currently in | |
| 659 // use: | |
| 660 // SkBlurDrawLooper is used for shadows. | |
| 661 // SkASSERT(!paint.getLooper()); | |
| 662 // SkASSERT(!paint.getMaskFilter()); | |
| 663 | |
| 664 // http://b/1165900 Implement xfermode. | |
| 665 // SkASSERT(!paint.getXfermode()); | |
| 666 | |
| 667 // The path effect should be processed before arriving here. | |
| 668 SkASSERT(!paint.getPathEffect()); | |
| 669 | |
| 670 // This isn't used in the code. Verify this assumption. | |
| 671 SkASSERT(!paint.getRasterizer()); | |
| 672 // Reuse code to load Win32 Fonts. | |
| 673 return true; | |
| 674 } | |
| 675 | |
| 676 void VectorPlatformDeviceEmf::setMatrixClip(const SkMatrix& transform, | |
| 677 const SkRegion& region, | |
| 678 const SkClipStack&) { | |
| 679 transform_ = transform; | |
| 680 LoadTransformToDC(hdc_, transform_); | |
| 681 clip_region_ = region; | |
| 682 if (!clip_region_.isEmpty()) | |
| 683 LoadClipRegion(); | |
| 684 } | |
| 685 | |
| 686 void VectorPlatformDeviceEmf::LoadClipRegion() { | |
| 687 SkMatrix t; | |
| 688 t.reset(); | |
| 689 LoadClippingRegionToDC(hdc_, clip_region_, t); | |
| 690 } | |
| 691 | |
| 692 SkBaseDevice* VectorPlatformDeviceEmf::onCreateCompatibleDevice( | |
| 693 const CreateInfo& info) { | |
| 694 SkASSERT(info.fInfo.colorType() == kN32_SkColorType); | |
| 695 return VectorPlatformDeviceEmf::CreateDevice( | |
| 696 info.fInfo.width(), info.fInfo.height(), info.fInfo.isOpaque(), NULL); | |
| 697 } | |
| 698 | |
| 699 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, COLORREF color) { | |
| 700 SkASSERT(previous_brush_ == NULL); | |
| 701 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer. | |
| 702 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use | |
| 703 // WHITE_BRUSH instead. | |
| 704 | |
| 705 if (!use_brush) { | |
| 706 // Set the transparency. | |
| 707 if (0 == SetBkMode(hdc_, TRANSPARENT)) { | |
| 708 SkASSERT(false); | |
| 709 return false; | |
| 710 } | |
| 711 | |
| 712 // Select the NULL brush. | |
| 713 previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH)); | |
| 714 return previous_brush_ != NULL; | |
| 715 } | |
| 716 | |
| 717 // Set the opacity. | |
| 718 if (0 == SetBkMode(hdc_, OPAQUE)) { | |
| 719 SkASSERT(false); | |
| 720 return false; | |
| 721 } | |
| 722 | |
| 723 // Create and select the brush. | |
| 724 previous_brush_ = SelectObject(CreateSolidBrush(color)); | |
| 725 return previous_brush_ != NULL; | |
| 726 } | |
| 727 | |
| 728 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, | |
| 729 COLORREF color, | |
| 730 int stroke_width, | |
| 731 float stroke_miter, | |
| 732 DWORD pen_style) { | |
| 733 SkASSERT(previous_pen_ == NULL); | |
| 734 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer. | |
| 735 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN | |
| 736 // instead. | |
| 737 | |
| 738 // No pen case | |
| 739 if (!use_pen) { | |
| 740 previous_pen_ = SelectObject(GetStockObject(NULL_PEN)); | |
| 741 return previous_pen_ != NULL; | |
| 742 } | |
| 743 | |
| 744 // Use the stock pen if the stroke width is 0. | |
| 745 if (stroke_width == 0) { | |
| 746 // Create a pen with the right color. | |
| 747 previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color)); | |
| 748 return previous_pen_ != NULL; | |
| 749 } | |
| 750 | |
| 751 // Load a custom pen. | |
| 752 LOGBRUSH brush = {0}; | |
| 753 brush.lbStyle = BS_SOLID; | |
| 754 brush.lbColor = color; | |
| 755 brush.lbHatch = 0; | |
| 756 HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL); | |
| 757 SkASSERT(pen != NULL); | |
| 758 previous_pen_ = SelectObject(pen); | |
| 759 if (previous_pen_ == NULL) | |
| 760 return false; | |
| 761 | |
| 762 if (!SetMiterLimit(hdc_, stroke_miter, NULL)) { | |
| 763 SkASSERT(false); | |
| 764 return false; | |
| 765 } | |
| 766 return true; | |
| 767 } | |
| 768 | |
| 769 void VectorPlatformDeviceEmf::Cleanup() { | |
| 770 if (previous_brush_) { | |
| 771 HGDIOBJ result = SelectObject(previous_brush_); | |
| 772 previous_brush_ = NULL; | |
| 773 if (result) { | |
| 774 BOOL res = DeleteObject(result); | |
| 775 SkASSERT(res != 0); | |
| 776 } | |
| 777 } | |
| 778 if (previous_pen_) { | |
| 779 HGDIOBJ result = SelectObject(previous_pen_); | |
| 780 previous_pen_ = NULL; | |
| 781 if (result) { | |
| 782 BOOL res = DeleteObject(result); | |
| 783 SkASSERT(res != 0); | |
| 784 } | |
| 785 } | |
| 786 // Remove any loaded path from the context. | |
| 787 AbortPath(hdc_); | |
| 788 } | |
| 789 | |
| 790 HGDIOBJ VectorPlatformDeviceEmf::SelectObject(HGDIOBJ object) { | |
| 791 HGDIOBJ result = ::SelectObject(hdc_, object); | |
| 792 SkASSERT(result != HGDI_ERROR); | |
| 793 if (result == HGDI_ERROR) | |
| 794 return NULL; | |
| 795 return result; | |
| 796 } | |
| 797 | |
| 798 bool VectorPlatformDeviceEmf::CreateBrush(bool use_brush, | |
| 799 const SkPaint& paint) { | |
| 800 // Make sure that for transparent color, no brush is used. | |
| 801 if (paint.getAlpha() == 0) { | |
| 802 use_brush = false; | |
| 803 } | |
| 804 | |
| 805 return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor())); | |
| 806 } | |
| 807 | |
| 808 bool VectorPlatformDeviceEmf::CreatePen(bool use_pen, const SkPaint& paint) { | |
| 809 // Make sure that for transparent color, no pen is used. | |
| 810 if (paint.getAlpha() == 0) { | |
| 811 use_pen = false; | |
| 812 } | |
| 813 | |
| 814 DWORD pen_style = PS_GEOMETRIC | PS_SOLID; | |
| 815 switch (paint.getStrokeJoin()) { | |
| 816 case SkPaint::kMiter_Join: | |
| 817 // Connects path segments with a sharp join. | |
| 818 pen_style |= PS_JOIN_MITER; | |
| 819 break; | |
| 820 case SkPaint::kRound_Join: | |
| 821 // Connects path segments with a round join. | |
| 822 pen_style |= PS_JOIN_ROUND; | |
| 823 break; | |
| 824 case SkPaint::kBevel_Join: | |
| 825 // Connects path segments with a flat bevel join. | |
| 826 pen_style |= PS_JOIN_BEVEL; | |
| 827 break; | |
| 828 default: | |
| 829 SkASSERT(false); | |
| 830 break; | |
| 831 } | |
| 832 switch (paint.getStrokeCap()) { | |
| 833 case SkPaint::kButt_Cap: | |
| 834 // Begin/end contours with no extension. | |
| 835 pen_style |= PS_ENDCAP_FLAT; | |
| 836 break; | |
| 837 case SkPaint::kRound_Cap: | |
| 838 // Begin/end contours with a semi-circle extension. | |
| 839 pen_style |= PS_ENDCAP_ROUND; | |
| 840 break; | |
| 841 case SkPaint::kSquare_Cap: | |
| 842 // Begin/end contours with a half square extension. | |
| 843 pen_style |= PS_ENDCAP_SQUARE; | |
| 844 break; | |
| 845 default: | |
| 846 SkASSERT(false); | |
| 847 break; | |
| 848 } | |
| 849 | |
| 850 return CreatePen(use_pen, | |
| 851 SkColorToCOLORREF(paint.getColor()), | |
| 852 SkScalarRoundToInt(paint.getStrokeWidth()), | |
| 853 paint.getStrokeMiter(), | |
| 854 pen_style); | |
| 855 } | |
| 856 | |
| 857 void VectorPlatformDeviceEmf::InternalDrawBitmap(const SkBitmap& bitmap, | |
| 858 int x, int y, | |
| 859 const SkPaint& paint) { | |
| 860 unsigned char alpha = paint.getAlpha(); | |
| 861 if (alpha == 0) | |
| 862 return; | |
| 863 | |
| 864 bool is_translucent; | |
| 865 if (alpha != 255) { | |
| 866 // ApplyPaint expect an opaque color. | |
| 867 SkPaint tmp_paint(paint); | |
| 868 tmp_paint.setAlpha(255); | |
| 869 if (!ApplyPaint(tmp_paint)) | |
| 870 return; | |
| 871 is_translucent = true; | |
| 872 } else { | |
| 873 if (!ApplyPaint(paint)) | |
| 874 return; | |
| 875 is_translucent = false; | |
| 876 } | |
| 877 int src_size_x = bitmap.width(); | |
| 878 int src_size_y = bitmap.height(); | |
| 879 if (!src_size_x || !src_size_y) | |
| 880 return; | |
| 881 | |
| 882 // Create a BMP v4 header that we can serialize. We use the shared "V3" | |
| 883 // fillter to fill the stardard items, then add in the "V4" stuff we want. | |
| 884 BITMAPV4HEADER bitmap_header = {0}; | |
| 885 FillBitmapInfoHeader(src_size_x, src_size_y, | |
| 886 reinterpret_cast<BITMAPINFOHEADER*>(&bitmap_header)); | |
| 887 bitmap_header.bV4Size = sizeof(BITMAPV4HEADER); | |
| 888 bitmap_header.bV4RedMask = 0x00ff0000; | |
| 889 bitmap_header.bV4GreenMask = 0x0000ff00; | |
| 890 bitmap_header.bV4BlueMask = 0x000000ff; | |
| 891 bitmap_header.bV4AlphaMask = 0xff000000; | |
| 892 | |
| 893 SkAutoLockPixels lock(bitmap); | |
| 894 SkASSERT(bitmap.colorType() == kN32_SkColorType); | |
| 895 const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels()); | |
| 896 if (pixels == NULL) { | |
| 897 SkASSERT(false); | |
| 898 return; | |
| 899 } | |
| 900 | |
| 901 if (!is_translucent) { | |
| 902 int row_length = bitmap.rowBytesAsPixels(); | |
| 903 // There is no quick way to determine if an image is opaque. | |
| 904 for (int y2 = 0; y2 < src_size_y; ++y2) { | |
| 905 for (int x2 = 0; x2 < src_size_x; ++x2) { | |
| 906 if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) { | |
| 907 is_translucent = true; | |
| 908 y2 = src_size_y; | |
| 909 break; | |
| 910 } | |
| 911 } | |
| 912 } | |
| 913 } | |
| 914 | |
| 915 HDC dc = BeginPlatformPaint(); | |
| 916 BITMAPINFOHEADER hdr = {0}; | |
| 917 FillBitmapInfoHeader(src_size_x, src_size_y, &hdr); | |
| 918 if (is_translucent) { | |
| 919 // The image must be loaded as a bitmap inside a device context. | |
| 920 HDC bitmap_dc = ::CreateCompatibleDC(dc); | |
| 921 void* bits = NULL; | |
| 922 HBITMAP hbitmap = ::CreateDIBSection( | |
| 923 bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr), | |
| 924 DIB_RGB_COLORS, &bits, NULL, 0); | |
| 925 | |
| 926 // static cast to a char so we can do byte ptr arithmatic to | |
| 927 // get the offset. | |
| 928 unsigned char* dest_buffer = static_cast<unsigned char *>(bits); | |
| 929 | |
| 930 // We will copy row by row to avoid having to worry about | |
| 931 // the row strides being different. | |
| 932 const int dest_row_size = hdr.biBitCount / 8 * hdr.biWidth; | |
| 933 for (int row = 0; row < bitmap.height(); ++row) { | |
| 934 int dest_offset = row * dest_row_size; | |
| 935 // pixels_offset in terms of pixel count. | |
| 936 int src_offset = row * bitmap.rowBytesAsPixels(); | |
| 937 memcpy(dest_buffer + dest_offset, pixels + src_offset, dest_row_size); | |
| 938 } | |
| 939 SkASSERT(hbitmap); | |
| 940 HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap); | |
| 941 | |
| 942 // After some analysis of IE7's behavior, this is the thing to do. I was | |
| 943 // sure IE7 was doing so kind of bitmasking due to the way translucent image | |
| 944 // where renderered but after some windbg tracing, it is being done by the | |
| 945 // printer driver after all (mostly HP printers). IE7 always use AlphaBlend | |
| 946 // for bitmasked images. The trick seems to switch the stretching mode in | |
| 947 // what the driver expects. | |
| 948 DWORD previous_mode = GetStretchBltMode(dc); | |
| 949 BOOL result = SetStretchBltMode(dc, COLORONCOLOR); | |
| 950 SkASSERT(result); | |
| 951 // Note that this function expect premultiplied colors (!) | |
| 952 BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; | |
| 953 result = GdiAlphaBlend(dc, | |
| 954 x, y, // Destination origin. | |
| 955 src_size_x, src_size_y, // Destination size. | |
| 956 bitmap_dc, | |
| 957 0, 0, // Source origin. | |
| 958 src_size_x, src_size_y, // Source size. | |
| 959 blend_function); | |
| 960 SkASSERT(result); | |
| 961 result = SetStretchBltMode(dc, previous_mode); | |
| 962 SkASSERT(result); | |
| 963 | |
| 964 ::SelectObject(bitmap_dc, static_cast<HBITMAP>(old_bitmap)); | |
| 965 DeleteObject(hbitmap); | |
| 966 DeleteDC(bitmap_dc); | |
| 967 } else { | |
| 968 int nCopied = StretchDIBits(dc, | |
| 969 x, y, // Destination origin. | |
| 970 src_size_x, src_size_y, | |
| 971 0, 0, // Source origin. | |
| 972 src_size_x, src_size_y, // Source size. | |
| 973 pixels, | |
| 974 reinterpret_cast<const BITMAPINFO*>(&hdr), | |
| 975 DIB_RGB_COLORS, | |
| 976 SRCCOPY); | |
| 977 } | |
| 978 EndPlatformPaint(); | |
| 979 Cleanup(); | |
| 980 } | |
| 981 | |
| 982 } // namespace skia | |
| OLD | NEW |