| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2006-2008 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 "base/gfx/vector_device.h" | |
| 6 | |
| 7 #include "base/gfx/gdi_util.h" | |
| 8 #include "base/gfx/skia_utils.h" | |
| 9 #include "base/logging.h" | |
| 10 #include "base/scoped_handle.h" | |
| 11 | |
| 12 #include "SkUtils.h" | |
| 13 | |
| 14 namespace gfx { | |
| 15 | |
| 16 VectorDevice* VectorDevice::create(HDC dc, int width, int height) { | |
| 17 InitializeDC(dc); | |
| 18 | |
| 19 // Link the SkBitmap to the current selected bitmap in the device context. | |
| 20 SkBitmap bitmap; | |
| 21 HGDIOBJ selected_bitmap = GetCurrentObject(dc, OBJ_BITMAP); | |
| 22 bool succeeded = false; | |
| 23 if (selected_bitmap != NULL) { | |
| 24 BITMAP bitmap_data; | |
| 25 if (GetObject(selected_bitmap, sizeof(BITMAP), &bitmap_data) == | |
| 26 sizeof(BITMAP)) { | |
| 27 // The context has a bitmap attached. Attach our SkBitmap to it. | |
| 28 // Warning: If the bitmap gets unselected from the HDC, VectorDevice has | |
| 29 // no way to detect this, so the HBITMAP could be released while SkBitmap | |
| 30 // still has a reference to it. Be cautious. | |
| 31 if (width == bitmap_data.bmWidth && | |
| 32 height == bitmap_data.bmHeight) { | |
| 33 bitmap.setConfig(SkBitmap::kARGB_8888_Config, | |
| 34 bitmap_data.bmWidth, | |
| 35 bitmap_data.bmHeight, | |
| 36 bitmap_data.bmWidthBytes); | |
| 37 bitmap.setPixels(bitmap_data.bmBits); | |
| 38 succeeded = true; | |
| 39 } | |
| 40 } | |
| 41 } | |
| 42 | |
| 43 if (!succeeded) | |
| 44 bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); | |
| 45 | |
| 46 return new VectorDevice(dc, bitmap); | |
| 47 } | |
| 48 | |
| 49 VectorDevice::VectorDevice(HDC dc, const SkBitmap& bitmap) | |
| 50 : PlatformDeviceWin(bitmap), | |
| 51 hdc_(dc), | |
| 52 previous_brush_(NULL), | |
| 53 previous_pen_(NULL) { | |
| 54 transform_.reset(); | |
| 55 } | |
| 56 | |
| 57 VectorDevice::~VectorDevice() { | |
| 58 DCHECK(previous_brush_ == NULL); | |
| 59 DCHECK(previous_pen_ == NULL); | |
| 60 } | |
| 61 | |
| 62 | |
| 63 void VectorDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { | |
| 64 // TODO(maruel): Bypass the current transformation matrix. | |
| 65 SkRect rect; | |
| 66 rect.fLeft = 0; | |
| 67 rect.fTop = 0; | |
| 68 rect.fRight = SkIntToScalar(width() + 1); | |
| 69 rect.fBottom = SkIntToScalar(height() + 1); | |
| 70 drawRect(draw, rect, paint); | |
| 71 } | |
| 72 | |
| 73 void VectorDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, | |
| 74 size_t count, const SkPoint pts[], | |
| 75 const SkPaint& paint) { | |
| 76 if (!count) | |
| 77 return; | |
| 78 | |
| 79 if (mode == SkCanvas::kPoints_PointMode) { | |
| 80 NOTREACHED(); | |
| 81 return; | |
| 82 } | |
| 83 | |
| 84 SkPaint tmp_paint(paint); | |
| 85 tmp_paint.setStyle(SkPaint::kStroke_Style); | |
| 86 | |
| 87 // Draw a path instead. | |
| 88 SkPath path; | |
| 89 switch (mode) { | |
| 90 case SkCanvas::kLines_PointMode: | |
| 91 if (count % 2) { | |
| 92 NOTREACHED(); | |
| 93 return; | |
| 94 } | |
| 95 for (size_t i = 0; i < count / 2; ++i) { | |
| 96 path.moveTo(pts[2 * i]); | |
| 97 path.lineTo(pts[2 * i + 1]); | |
| 98 } | |
| 99 break; | |
| 100 case SkCanvas::kPolygon_PointMode: | |
| 101 path.moveTo(pts[0]); | |
| 102 for (size_t i = 1; i < count; ++i) { | |
| 103 path.lineTo(pts[i]); | |
| 104 } | |
| 105 break; | |
| 106 default: | |
| 107 NOTREACHED(); | |
| 108 return; | |
| 109 } | |
| 110 // Draw the calculated path. | |
| 111 drawPath(draw, path, tmp_paint); | |
| 112 } | |
| 113 | |
| 114 void VectorDevice::drawRect(const SkDraw& draw, const SkRect& rect, | |
| 115 const SkPaint& paint) { | |
| 116 if (paint.getPathEffect()) { | |
| 117 // Draw a path instead. | |
| 118 SkPath path_orginal; | |
| 119 path_orginal.addRect(rect); | |
| 120 | |
| 121 // Apply the path effect to the rect. | |
| 122 SkPath path_modified; | |
| 123 paint.getFillPath(path_orginal, &path_modified); | |
| 124 | |
| 125 // Removes the path effect from the temporary SkPaint object. | |
| 126 SkPaint paint_no_effet(paint); | |
| 127 paint_no_effet.setPathEffect(NULL)->safeUnref(); | |
| 128 | |
| 129 // Draw the calculated path. | |
| 130 drawPath(draw, path_modified, paint_no_effet); | |
| 131 return; | |
| 132 } | |
| 133 | |
| 134 if (!ApplyPaint(paint)) { | |
| 135 return; | |
| 136 } | |
| 137 HDC dc = getBitmapDC(); | |
| 138 if (!Rectangle(dc, SkScalarRound(rect.fLeft), | |
| 139 SkScalarRound(rect.fTop), | |
| 140 SkScalarRound(rect.fRight), | |
| 141 SkScalarRound(rect.fBottom))) { | |
| 142 NOTREACHED(); | |
| 143 } | |
| 144 Cleanup(); | |
| 145 } | |
| 146 | |
| 147 void VectorDevice::drawPath(const SkDraw& draw, const SkPath& path, | |
| 148 const SkPaint& paint) { | |
| 149 if (paint.getPathEffect()) { | |
| 150 // Apply the path effect forehand. | |
| 151 SkPath path_modified; | |
| 152 paint.getFillPath(path, &path_modified); | |
| 153 | |
| 154 // Removes the path effect from the temporary SkPaint object. | |
| 155 SkPaint paint_no_effet(paint); | |
| 156 paint_no_effet.setPathEffect(NULL)->safeUnref(); | |
| 157 | |
| 158 // Draw the calculated path. | |
| 159 drawPath(draw, path_modified, paint_no_effet); | |
| 160 return; | |
| 161 } | |
| 162 | |
| 163 if (!ApplyPaint(paint)) { | |
| 164 return; | |
| 165 } | |
| 166 HDC dc = getBitmapDC(); | |
| 167 PlatformDeviceWin::LoadPathToDC(dc, path); | |
| 168 switch (paint.getStyle()) { | |
| 169 case SkPaint::kFill_Style: { | |
| 170 BOOL res = StrokeAndFillPath(dc); | |
| 171 DCHECK(res != 0); | |
| 172 break; | |
| 173 } | |
| 174 case SkPaint::kStroke_Style: { | |
| 175 BOOL res = StrokePath(dc); | |
| 176 DCHECK(res != 0); | |
| 177 break; | |
| 178 } | |
| 179 case SkPaint::kStrokeAndFill_Style: { | |
| 180 BOOL res = StrokeAndFillPath(dc); | |
| 181 DCHECK(res != 0); | |
| 182 break; | |
| 183 } | |
| 184 default: | |
| 185 NOTREACHED(); | |
| 186 break; | |
| 187 } | |
| 188 Cleanup(); | |
| 189 } | |
| 190 | |
| 191 void VectorDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, | |
| 192 const SkMatrix& matrix, const SkPaint& paint) { | |
| 193 // Load the temporary matrix. This is what will translate, rotate and resize | |
| 194 // the bitmap. | |
| 195 SkMatrix actual_transform(transform_); | |
| 196 actual_transform.preConcat(matrix); | |
| 197 LoadTransformToDC(hdc_, actual_transform); | |
| 198 | |
| 199 InternalDrawBitmap(bitmap, 0, 0, paint); | |
| 200 | |
| 201 // Restore the original matrix. | |
| 202 LoadTransformToDC(hdc_, transform_); | |
| 203 } | |
| 204 | |
| 205 void VectorDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, | |
| 206 int x, int y, const SkPaint& paint) { | |
| 207 SkMatrix identity; | |
| 208 identity.reset(); | |
| 209 LoadTransformToDC(hdc_, identity); | |
| 210 | |
| 211 InternalDrawBitmap(bitmap, x, y, paint); | |
| 212 | |
| 213 // Restore the original matrix. | |
| 214 LoadTransformToDC(hdc_, transform_); | |
| 215 } | |
| 216 | |
| 217 void VectorDevice::drawText(const SkDraw& draw, const void* text, size_t byteLen
gth, | |
| 218 SkScalar x, SkScalar y, const SkPaint& paint) { | |
| 219 // This function isn't used in the code. Verify this assumption. | |
| 220 NOTREACHED(); | |
| 221 } | |
| 222 | |
| 223 void VectorDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, | |
| 224 const SkScalar pos[], SkScalar constY, | |
| 225 int scalarsPerPos, const SkPaint& paint) { | |
| 226 // This function isn't used in the code. Verify this assumption. | |
| 227 NOTREACHED(); | |
| 228 } | |
| 229 | |
| 230 void VectorDevice::drawTextOnPath(const SkDraw& draw, const void* text, | |
| 231 size_t len, | |
| 232 const SkPath& path, const SkMatrix* matrix, | |
| 233 const SkPaint& paint) { | |
| 234 // This function isn't used in the code. Verify this assumption. | |
| 235 NOTREACHED(); | |
| 236 } | |
| 237 | |
| 238 void VectorDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, | |
| 239 int vertexCount, | |
| 240 const SkPoint vertices[], const SkPoint texs[], | |
| 241 const SkColor colors[], SkXfermode* xmode, | |
| 242 const uint16_t indices[], int indexCount, | |
| 243 const SkPaint& paint) { | |
| 244 // This function isn't used in the code. Verify this assumption. | |
| 245 NOTREACHED(); | |
| 246 } | |
| 247 | |
| 248 void VectorDevice::drawDevice(const SkDraw& draw, SkDevice* device, int x, | |
| 249 int y, const SkPaint& paint) { | |
| 250 // TODO(maruel): http://b/1183870 Playback the EMF buffer at printer's dpi if | |
| 251 // it is a vectorial device. | |
| 252 drawSprite(draw, device->accessBitmap(false), x, y, paint); | |
| 253 } | |
| 254 | |
| 255 bool VectorDevice::ApplyPaint(const SkPaint& paint) { | |
| 256 // Note: The goal here is to transfert the SkPaint's state to the HDC's state. | |
| 257 // This function does not execute the SkPaint drawing commands. These should | |
| 258 // be executed in drawPaint(). | |
| 259 | |
| 260 SkPaint::Style style = paint.getStyle(); | |
| 261 if (!paint.getAlpha()) | |
| 262 style = SkPaint::kStyleCount; | |
| 263 | |
| 264 switch (style) { | |
| 265 case SkPaint::kFill_Style: | |
| 266 if (!CreateBrush(true, paint) || | |
| 267 !CreatePen(false, paint)) | |
| 268 return false; | |
| 269 break; | |
| 270 case SkPaint::kStroke_Style: | |
| 271 if (!CreateBrush(false, paint) || | |
| 272 !CreatePen(true, paint)) | |
| 273 return false; | |
| 274 break; | |
| 275 case SkPaint::kStrokeAndFill_Style: | |
| 276 if (!CreateBrush(true, paint) || | |
| 277 !CreatePen(true, paint)) | |
| 278 return false; | |
| 279 break; | |
| 280 default: | |
| 281 if (!CreateBrush(false, paint) || | |
| 282 !CreatePen(false, paint)) | |
| 283 return false; | |
| 284 break; | |
| 285 } | |
| 286 | |
| 287 /* | |
| 288 getFlags(); | |
| 289 isAntiAlias(); | |
| 290 isDither() | |
| 291 isLinearText() | |
| 292 isSubpixelText() | |
| 293 isUnderlineText() | |
| 294 isStrikeThruText() | |
| 295 isFakeBoldText() | |
| 296 isDevKernText() | |
| 297 isFilterBitmap() | |
| 298 | |
| 299 // Skia's text is not used. This should be fixed. | |
| 300 getTextAlign() | |
| 301 getTextScaleX() | |
| 302 getTextSkewX() | |
| 303 getTextEncoding() | |
| 304 getFontMetrics() | |
| 305 getFontSpacing() | |
| 306 */ | |
| 307 | |
| 308 // BUG 1094907: Implement shaders. Shaders currently in use: | |
| 309 // SkShader::CreateBitmapShader | |
| 310 // SkGradientShader::CreateRadial | |
| 311 // SkGradientShader::CreateLinear | |
| 312 // DCHECK(!paint.getShader()); | |
| 313 | |
| 314 // http://b/1106647 Implement loopers and mask filter. Looper currently in | |
| 315 // use: | |
| 316 // SkBlurDrawLooper is used for shadows. | |
| 317 // DCHECK(!paint.getLooper()); | |
| 318 // DCHECK(!paint.getMaskFilter()); | |
| 319 | |
| 320 // http://b/1165900 Implement xfermode. | |
| 321 // DCHECK(!paint.getXfermode()); | |
| 322 | |
| 323 // The path effect should be processed before arriving here. | |
| 324 DCHECK(!paint.getPathEffect()); | |
| 325 | |
| 326 // These aren't used in the code. Verify this assumption. | |
| 327 DCHECK(!paint.getColorFilter()); | |
| 328 DCHECK(!paint.getRasterizer()); | |
| 329 // Reuse code to load Win32 Fonts. | |
| 330 DCHECK(!paint.getTypeface()); | |
| 331 return true; | |
| 332 } | |
| 333 | |
| 334 void VectorDevice::setMatrixClip(const SkMatrix& transform, | |
| 335 const SkRegion& region) { | |
| 336 transform_ = transform; | |
| 337 LoadTransformToDC(hdc_, transform_); | |
| 338 clip_region_ = region; | |
| 339 if (!clip_region_.isEmpty()) | |
| 340 LoadClipRegion(); | |
| 341 } | |
| 342 | |
| 343 void VectorDevice::drawToHDC(HDC dc, int x, int y, const RECT* src_rect) { | |
| 344 NOTREACHED(); | |
| 345 } | |
| 346 | |
| 347 void VectorDevice::LoadClipRegion() { | |
| 348 SkMatrix t; | |
| 349 t.reset(); | |
| 350 LoadClippingRegionToDC(hdc_, clip_region_, t); | |
| 351 } | |
| 352 | |
| 353 bool VectorDevice::CreateBrush(bool use_brush, COLORREF color) { | |
| 354 DCHECK(previous_brush_ == NULL); | |
| 355 // We can't use SetDCBrushColor() or DC_BRUSH when drawing to a EMF buffer. | |
| 356 // SetDCBrushColor() calls are not recorded at all and DC_BRUSH will use | |
| 357 // WHITE_BRUSH instead. | |
| 358 | |
| 359 if (!use_brush) { | |
| 360 // Set the transparency. | |
| 361 if (0 == SetBkMode(hdc_, TRANSPARENT)) { | |
| 362 NOTREACHED(); | |
| 363 return false; | |
| 364 } | |
| 365 | |
| 366 // Select the NULL brush. | |
| 367 previous_brush_ = SelectObject(GetStockObject(NULL_BRUSH)); | |
| 368 return previous_brush_ != NULL; | |
| 369 } | |
| 370 | |
| 371 // Set the opacity. | |
| 372 if (0 == SetBkMode(hdc_, OPAQUE)) { | |
| 373 NOTREACHED(); | |
| 374 return false; | |
| 375 } | |
| 376 | |
| 377 // Create and select the brush. | |
| 378 previous_brush_ = SelectObject(CreateSolidBrush(color)); | |
| 379 return previous_brush_ != NULL; | |
| 380 } | |
| 381 | |
| 382 bool VectorDevice::CreatePen(bool use_pen, COLORREF color, int stroke_width, | |
| 383 float stroke_miter, DWORD pen_style) { | |
| 384 DCHECK(previous_pen_ == NULL); | |
| 385 // We can't use SetDCPenColor() or DC_PEN when drawing to a EMF buffer. | |
| 386 // SetDCPenColor() calls are not recorded at all and DC_PEN will use BLACK_PEN | |
| 387 // instead. | |
| 388 | |
| 389 // No pen case | |
| 390 if (!use_pen) { | |
| 391 previous_pen_ = SelectObject(GetStockObject(NULL_PEN)); | |
| 392 return previous_pen_ != NULL; | |
| 393 } | |
| 394 | |
| 395 // Use the stock pen if the stroke width is 0. | |
| 396 if (stroke_width == 0) { | |
| 397 // Create a pen with the right color. | |
| 398 previous_pen_ = SelectObject(::CreatePen(PS_SOLID, 0, color)); | |
| 399 return previous_pen_ != NULL; | |
| 400 } | |
| 401 | |
| 402 // Load a custom pen. | |
| 403 LOGBRUSH brush; | |
| 404 brush.lbStyle = BS_SOLID; | |
| 405 brush.lbColor = color; | |
| 406 brush.lbHatch = 0; | |
| 407 HPEN pen = ExtCreatePen(pen_style, stroke_width, &brush, 0, NULL); | |
| 408 DCHECK(pen != NULL); | |
| 409 previous_pen_ = SelectObject(pen); | |
| 410 if (previous_pen_ == NULL) | |
| 411 return false; | |
| 412 | |
| 413 if (!SetMiterLimit(hdc_, stroke_miter, NULL)) { | |
| 414 NOTREACHED(); | |
| 415 return false; | |
| 416 } | |
| 417 return true; | |
| 418 } | |
| 419 | |
| 420 void VectorDevice::Cleanup() { | |
| 421 if (previous_brush_) { | |
| 422 HGDIOBJ result = SelectObject(previous_brush_); | |
| 423 previous_brush_ = NULL; | |
| 424 if (result) { | |
| 425 BOOL res = DeleteObject(result); | |
| 426 DCHECK(res != 0); | |
| 427 } | |
| 428 } | |
| 429 if (previous_pen_) { | |
| 430 HGDIOBJ result = SelectObject(previous_pen_); | |
| 431 previous_pen_ = NULL; | |
| 432 if (result) { | |
| 433 BOOL res = DeleteObject(result); | |
| 434 DCHECK(res != 0); | |
| 435 } | |
| 436 } | |
| 437 // Remove any loaded path from the context. | |
| 438 AbortPath(hdc_); | |
| 439 } | |
| 440 | |
| 441 HGDIOBJ VectorDevice::SelectObject(HGDIOBJ object) { | |
| 442 HGDIOBJ result = ::SelectObject(hdc_, object); | |
| 443 DCHECK(result != HGDI_ERROR); | |
| 444 if (result == HGDI_ERROR) | |
| 445 return NULL; | |
| 446 return result; | |
| 447 } | |
| 448 | |
| 449 bool VectorDevice::CreateBrush(bool use_brush, const SkPaint& paint) { | |
| 450 // Make sure that for transparent color, no brush is used. | |
| 451 if (paint.getAlpha() == 0) { | |
| 452 // Test if it ever happen. | |
| 453 NOTREACHED(); | |
| 454 use_brush = false; | |
| 455 } | |
| 456 | |
| 457 return CreateBrush(use_brush, SkColorToCOLORREF(paint.getColor())); | |
| 458 } | |
| 459 | |
| 460 bool VectorDevice::CreatePen(bool use_pen, const SkPaint& paint) { | |
| 461 // Make sure that for transparent color, no pen is used. | |
| 462 if (paint.getAlpha() == 0) { | |
| 463 // Test if it ever happen. | |
| 464 NOTREACHED(); | |
| 465 use_pen = false; | |
| 466 } | |
| 467 | |
| 468 DWORD pen_style = PS_GEOMETRIC | PS_SOLID; | |
| 469 switch (paint.getStrokeJoin()) { | |
| 470 case SkPaint::kMiter_Join: | |
| 471 // Connects path segments with a sharp join. | |
| 472 pen_style |= PS_JOIN_MITER; | |
| 473 break; | |
| 474 case SkPaint::kRound_Join: | |
| 475 // Connects path segments with a round join. | |
| 476 pen_style |= PS_JOIN_ROUND; | |
| 477 break; | |
| 478 case SkPaint::kBevel_Join: | |
| 479 // Connects path segments with a flat bevel join. | |
| 480 pen_style |= PS_JOIN_BEVEL; | |
| 481 break; | |
| 482 default: | |
| 483 NOTREACHED(); | |
| 484 break; | |
| 485 } | |
| 486 switch (paint.getStrokeCap()) { | |
| 487 case SkPaint::kButt_Cap: | |
| 488 // Begin/end contours with no extension. | |
| 489 pen_style |= PS_ENDCAP_FLAT; | |
| 490 break; | |
| 491 case SkPaint::kRound_Cap: | |
| 492 // Begin/end contours with a semi-circle extension. | |
| 493 pen_style |= PS_ENDCAP_ROUND; | |
| 494 break; | |
| 495 case SkPaint::kSquare_Cap: | |
| 496 // Begin/end contours with a half square extension. | |
| 497 pen_style |= PS_ENDCAP_SQUARE; | |
| 498 break; | |
| 499 default: | |
| 500 NOTREACHED(); | |
| 501 break; | |
| 502 } | |
| 503 | |
| 504 return CreatePen(use_pen, | |
| 505 SkColorToCOLORREF(paint.getColor()), | |
| 506 SkScalarRound(paint.getStrokeWidth()), | |
| 507 paint.getStrokeMiter(), | |
| 508 pen_style); | |
| 509 } | |
| 510 | |
| 511 void VectorDevice::InternalDrawBitmap(const SkBitmap& bitmap, int x, int y, | |
| 512 const SkPaint& paint) { | |
| 513 uint8 alpha = paint.getAlpha(); | |
| 514 if (alpha == 0) | |
| 515 return; | |
| 516 | |
| 517 bool is_translucent; | |
| 518 if (alpha != 255) { | |
| 519 // ApplyPaint expect an opaque color. | |
| 520 SkPaint tmp_paint(paint); | |
| 521 tmp_paint.setAlpha(255); | |
| 522 if (!ApplyPaint(tmp_paint)) | |
| 523 return; | |
| 524 is_translucent = true; | |
| 525 } else { | |
| 526 if (!ApplyPaint(paint)) | |
| 527 return; | |
| 528 is_translucent = false; | |
| 529 } | |
| 530 int src_size_x = bitmap.width(); | |
| 531 int src_size_y = bitmap.height(); | |
| 532 if (!src_size_x || !src_size_y) | |
| 533 return; | |
| 534 | |
| 535 // Create a BMP v4 header that we can serialize. | |
| 536 BITMAPV4HEADER bitmap_header; | |
| 537 gfx::CreateBitmapV4Header(src_size_x, src_size_y, &bitmap_header); | |
| 538 HDC dc = getBitmapDC(); | |
| 539 SkAutoLockPixels lock(bitmap); | |
| 540 DCHECK_EQ(bitmap.getConfig(), SkBitmap::kARGB_8888_Config); | |
| 541 const uint32_t* pixels = static_cast<const uint32_t*>(bitmap.getPixels()); | |
| 542 if (pixels == NULL) { | |
| 543 NOTREACHED(); | |
| 544 return; | |
| 545 } | |
| 546 | |
| 547 if (!is_translucent) { | |
| 548 int row_length = bitmap.rowBytesAsPixels(); | |
| 549 // There is no quick way to determine if an image is opaque. | |
| 550 for (int y2 = 0; y2 < src_size_y; ++y2) { | |
| 551 for (int x2 = 0; x2 < src_size_x; ++x2) { | |
| 552 if (SkColorGetA(pixels[(y2 * row_length) + x2]) != 255) { | |
| 553 is_translucent = true; | |
| 554 y2 = src_size_y; | |
| 555 break; | |
| 556 } | |
| 557 } | |
| 558 } | |
| 559 } | |
| 560 | |
| 561 BITMAPINFOHEADER hdr; | |
| 562 gfx::CreateBitmapHeader(src_size_x, src_size_y, &hdr); | |
| 563 if (is_translucent) { | |
| 564 // The image must be loaded as a bitmap inside a device context. | |
| 565 ScopedHDC bitmap_dc(::CreateCompatibleDC(dc)); | |
| 566 void* bits = NULL; | |
| 567 ScopedBitmap hbitmap(::CreateDIBSection( | |
| 568 bitmap_dc, reinterpret_cast<const BITMAPINFO*>(&hdr), | |
| 569 DIB_RGB_COLORS, &bits, NULL, 0)); | |
| 570 memcpy(bits, pixels, bitmap.getSize()); | |
| 571 DCHECK(hbitmap); | |
| 572 HGDIOBJ old_bitmap = ::SelectObject(bitmap_dc, hbitmap); | |
| 573 DeleteObject(old_bitmap); | |
| 574 | |
| 575 // After some analysis of IE7's behavior, this is the thing to do. I was | |
| 576 // sure IE7 was doing so kind of bitmasking due to the way translucent image | |
| 577 // where renderered but after some windbg tracing, it is being done by the | |
| 578 // printer driver after all (mostly HP printers). IE7 always use AlphaBlend | |
| 579 // for bitmasked images. The trick seems to switch the stretching mode in | |
| 580 // what the driver expects. | |
| 581 DWORD previous_mode = GetStretchBltMode(dc); | |
| 582 BOOL result = SetStretchBltMode(dc, COLORONCOLOR); | |
| 583 DCHECK(result); | |
| 584 // Note that this function expect premultiplied colors (!) | |
| 585 BLENDFUNCTION blend_function = {AC_SRC_OVER, 0, alpha, AC_SRC_ALPHA}; | |
| 586 result = GdiAlphaBlend(dc, | |
| 587 x, y, // Destination origin. | |
| 588 src_size_x, src_size_y, // Destination size. | |
| 589 bitmap_dc, | |
| 590 0, 0, // Source origin. | |
| 591 src_size_x, src_size_y, // Source size. | |
| 592 blend_function); | |
| 593 DCHECK(result); | |
| 594 result = SetStretchBltMode(dc, previous_mode); | |
| 595 DCHECK(result); | |
| 596 } else { | |
| 597 BOOL result = StretchDIBits(dc, | |
| 598 x, y, // Destination origin. | |
| 599 src_size_x, src_size_y, | |
| 600 0, 0, // Source origin. | |
| 601 src_size_x, src_size_y, // Source size. | |
| 602 pixels, | |
| 603 reinterpret_cast<const BITMAPINFO*>(&hdr), | |
| 604 DIB_RGB_COLORS, | |
| 605 SRCCOPY); | |
| 606 DCHECK(result); | |
| 607 } | |
| 608 Cleanup(); | |
| 609 } | |
| 610 | |
| 611 } // namespace gfx | |
| 612 | |
| OLD | NEW |