1 /* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <windows.h> 32 #include "config.h" 33 34 #include "core/platform/graphics/GraphicsContext.h" 35 #include "core/platform/graphics/ImageBuffer.h" 36 #include "core/platform/graphics/SimpleFontData.h" 37 #include "core/platform/graphics/chromium/TransparencyWin.h" 38 #include "core/platform/graphics/transforms/AffineTransform.h" 39 40 #include "SkColorPriv.h" 41 #include "skia/ext/platform_canvas.h" 42 43 namespace WebCore { 44 45 namespace { 46 47 // The maximum size in pixels of the buffer we'll keep around for drawing text 48 // into. Buffers larger than this will be destroyed when we're done with them. 49 const int maxCachedBufferPixelSize = 65536; 50 51 inline const SkBitmap& bitmapForContext(const GraphicsContext& context) 52 { 53 return context.layerBitmap(); 54 } 55 56 void compositeToCopy(GraphicsContext& sourceLayers, 57 GraphicsContext& destContext, 58 const AffineTransform& matrix) 59 { 60 // Make a list of all devices. The iterator goes top-down, and we want 61 // bottom-up. Note that each layer can also have an offset in canvas 62 // coordinates, which is the (x, y) position. 63 struct DeviceInfo { 64 DeviceInfo(SkDevice* d, int lx, int ly) 65 : device(d) 66 , x(lx) 67 , y(ly) {} 68 SkDevice* device; 69 int x; 70 int y; 71 }; 72 Vector<DeviceInfo> devices; 73 SkCanvas* sourceCanvas = sourceLayers.canvas(); 74 SkCanvas::LayerIter iter(sourceCanvas, false); 75 while (!iter.done()) { 76 devices.append(DeviceInfo(iter.device(), iter.x(), iter.y())); 77 iter.next(); 78 } 79 80 // Create a temporary canvas for the compositing into the destination. 81 SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext)); 82 SkCanvas destCanvas(*destBmp); 83 destCanvas.setMatrix(matrix); 84 85 for (int i = devices.size() - 1; i >= 0; i--) { 86 const SkBitmap& srcBmp = devices[i].device->accessBitmap(false); 87 88 SkRect destRect; 89 destRect.fLeft = devices[i].x; 90 destRect.fTop = devices[i].y; 91 destRect.fRight = destRect.fLeft + srcBmp.width(); 92 destRect.fBottom = destRect.fTop + srcBmp.height(); 93 94 destCanvas.drawBitmapRect(srcBmp, 0, destRect); 95 } 96 } 97 98 } // namespace 99 100 // If either of these pointers is non-null, both must be valid and point to 101 // bitmaps of the same size. 102 class TransparencyWin::OwnedBuffers { 103 public: 104 OwnedBuffers(const IntSize& size, bool needReferenceBuffer) 105 { 106 m_destBitmap = ImageBuffer::create(size, 1); 107 108 if (needReferenceBuffer) { 109 m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); 110 m_referenceBitmap.allocPixels(); 111 m_referenceBitmap.eraseARGB(0, 0, 0, 0); 112 } 113 } 114 115 ImageBuffer* destBitmap() { return m_destBitmap.get(); } 116 117 // This bitmap will be empty if you don't specify needReferenceBuffer to the 118 // constructor. 119 SkBitmap* referenceBitmap() { return &m_referenceBitmap; } 120 121 // Returns whether the current layer will fix a buffer of the given size. 122 bool canHandleSize(const IntSize& size) const 123 { 124 return m_destBitmap->internalSize().width() >= size.width() && m_destBitmap->internalSize().height() >= size.height(); 125 } 126 127 private: 128 // The destination bitmap we're drawing into. 129 OwnPtr<ImageBuffer> m_destBitmap; 130 131 // This could be an ImageBuffer but this is an optimization. Since this is 132 // only ever used as a reference, we don't need to make a full 133 // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap 134 // is much faster since it's just a Malloc rather than a GDI call. 135 SkBitmap m_referenceBitmap; 136 }; 137 138 TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0; 139 140 TransparencyWin::TransparencyWin() 141 : m_destContext(0) 142 , m_orgTransform() 143 , m_layerMode(NoLayer) 144 , m_transformMode(KeepTransform) 145 , m_drawContext(0) 146 , m_savedOnDrawContext(false) 147 , m_layerBuffer(0) 148 , m_referenceBitmap(0) 149 , m_validLayer(false) 150 { 151 } 152 153 TransparencyWin::~TransparencyWin() 154 { 155 // This should be false, since calling composite() is mandatory. 156 ASSERT(!m_savedOnDrawContext); 157 } 158 159 void TransparencyWin::composite() 160 { 161 // Matches the save() in initializeNewTextContext (or the constructor for 162 // SCALE) to put the context back into the same state we found it. 163 if (m_savedOnDrawContext) { 164 m_drawContext->restore(); 165 m_savedOnDrawContext = false; 166 } 167 168 switch (m_layerMode) { 169 case NoLayer: 170 break; 171 case OpaqueCompositeLayer: 172 case WhiteLayer: 173 compositeOpaqueComposite(); 174 break; 175 case TextComposite: 176 compositeTextComposite(); 177 break; 178 } 179 } 180 181 void TransparencyWin::init(GraphicsContext* dest, 182 LayerMode layerMode, 183 TransformMode transformMode, 184 const IntRect& region) 185 { 186 m_destContext = dest; 187 m_orgTransform = dest->getCTM(); 188 m_layerMode = layerMode; 189 m_transformMode = transformMode; 190 m_sourceRect = region; 191 192 computeLayerSize(); 193 setupLayer(); 194 setupTransform(region); 195 } 196 197 void TransparencyWin::computeLayerSize() 198 { 199 if (m_transformMode == Untransform) { 200 // The meaning of the "transformed" source rect is a little ambigous 201 // here. The rest of the code doesn't care about it in the Untransform 202 // case since we're using our own happy coordinate system. So we set it 203 // to be the source rect since that matches how the code below actually 204 // uses the variable: to determine how to translate things to account 205 // for the offset of the layer. 206 m_transformedSourceRect = m_sourceRect; 207 } else if (m_transformMode == KeepTransform && m_layerMode != TextComposite) { 208 // FIXME: support clipping for other modes 209 IntRect clippedSourceRect = m_sourceRect; 210 SkRect clipBounds; 211 if (m_destContext->getClipBounds(&clipBounds)) { 212 FloatRect clipRect(clipBounds.left(), clipBounds.top(), clipBounds.width(), clipBounds.height()); 213 clippedSourceRect.intersect(enclosingIntRect(clipRect)); 214 } 215 m_transformedSourceRect = m_orgTransform.mapRect(clippedSourceRect); 216 } else 217 m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect); 218 219 m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height()); 220 } 221 222 void TransparencyWin::setupLayer() 223 { 224 switch (m_layerMode) { 225 case NoLayer: 226 setupLayerForNoLayer(); 227 break; 228 case OpaqueCompositeLayer: 229 setupLayerForOpaqueCompositeLayer(); 230 break; 231 case TextComposite: 232 setupLayerForTextComposite(); 233 break; 234 case WhiteLayer: 235 setupLayerForWhiteLayer(); 236 break; 237 } 238 } 239 240 void TransparencyWin::setupLayerForNoLayer() 241 { 242 m_drawContext = m_destContext; // Draw to the source context. 243 m_validLayer = true; 244 } 245 246 void TransparencyWin::setupLayerForOpaqueCompositeLayer() 247 { 248 initializeNewContext(); 249 if (!m_validLayer) 250 return; 251 252 AffineTransform mapping; 253 mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); 254 if (m_transformMode == Untransform){ 255 // Compute the inverse mapping from the canvas space to the 256 // coordinate space of our bitmap. 257 mapping *= m_orgTransform.inverse(); 258 } 259 compositeToCopy(*m_destContext, *m_drawContext, mapping); 260 261 // Save the reference layer so we can tell what changed. 262 SkCanvas referenceCanvas(*m_referenceBitmap); 263 referenceCanvas.drawBitmap(bitmapForContext(*m_drawContext), 0, 0); 264 // Layer rect represents the part of the original layer. 265 } 266 267 void TransparencyWin::setupLayerForTextComposite() 268 { 269 ASSERT(m_transformMode == KeepTransform); 270 // Fall through to filling with white. 271 setupLayerForWhiteLayer(); 272 } 273 274 void TransparencyWin::setupLayerForWhiteLayer() 275 { 276 initializeNewContext(); 277 if (!m_validLayer) 278 return; 279 280 m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white); 281 // Layer rect represents the part of the original layer. 282 } 283 284 void TransparencyWin::setupTransform(const IntRect& region) 285 { 286 switch (m_transformMode) { 287 case KeepTransform: 288 setupTransformForKeepTransform(region); 289 break; 290 case Untransform: 291 setupTransformForUntransform(); 292 break; 293 case ScaleTransform: 294 setupTransformForScaleTransform(); 295 break; 296 } 297 } 298 299 void TransparencyWin::setupTransformForKeepTransform(const IntRect& region) 300 { 301 if (!m_validLayer) 302 return; 303 304 if (m_layerMode != NoLayer) { 305 // Need to save things since we're modifying the transform. 306 m_drawContext->save(); 307 m_savedOnDrawContext = true; 308 309 // Account for the fact that the layer may be offset from the 310 // original. This only happens when we create a layer that has the 311 // same coordinate space as the parent. 312 AffineTransform xform; 313 xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); 314 315 // We're making a layer, so apply the old transform to the new one 316 // so it's maintained. We know the new layer has the identity 317 // transform now, we we can just multiply it. 318 xform *= m_orgTransform; 319 m_drawContext->concatCTM(xform); 320 } 321 m_drawRect = m_sourceRect; 322 } 323 324 void TransparencyWin::setupTransformForUntransform() 325 { 326 ASSERT(m_layerMode != NoLayer); 327 // We now have a new layer with the identity transform, which is the 328 // Untransformed space we'll use for drawing. 329 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); 330 } 331 332 void TransparencyWin::setupTransformForScaleTransform() 333 { 334 if (!m_validLayer) 335 return; 336 337 if (m_layerMode == NoLayer) { 338 // Need to save things since we're modifying the layer. 339 m_drawContext->save(); 340 m_savedOnDrawContext = true; 341 342 // Undo the transform on the current layer when we're re-using the 343 // current one. 344 m_drawContext->concatCTM(m_drawContext->getCTM().inverse()); 345 346 // We're drawing to the original layer with just a different size. 347 m_drawRect = m_transformedSourceRect; 348 } else { 349 // Just go ahead and use the layer's coordinate space to draw into. 350 // It will have the scaled size, and an identity transform loaded. 351 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); 352 } 353 } 354 355 void TransparencyWin::setTextCompositeColor(Color color) 356 { 357 m_textCompositeColor = color; 358 } 359 360 void TransparencyWin::initializeNewContext() 361 { 362 int pixelSize = m_layerSize.width() * m_layerSize.height(); 363 if (pixelSize <= 0) 364 return; 365 366 if (pixelSize > maxCachedBufferPixelSize) { 367 // Create a 1-off buffer for drawing into. We only need the reference 368 // buffer if we're making an OpaqueCompositeLayer. 369 bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer; 370 m_ownedBuffers = adoptPtr(new OwnedBuffers(m_layerSize, needReferenceBitmap)); 371 m_layerBuffer = m_ownedBuffers->destBitmap(); 372 if (!m_layerBuffer) 373 return; 374 375 m_drawContext = m_layerBuffer->context(); 376 if (needReferenceBitmap) { 377 m_referenceBitmap = m_ownedBuffers->referenceBitmap(); 378 if (!m_referenceBitmap || !m_referenceBitmap->getPixels()) 379 return; 380 } 381 m_validLayer = true; 382 return; 383 } 384 385 if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) { 386 // We can re-use the existing buffer. We don't need to clear it since 387 // all layer modes will clear it in their initialization. 388 m_layerBuffer = m_cachedBuffers->destBitmap(); 389 m_drawContext = m_cachedBuffers->destBitmap()->context(); 390 bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0); 391 m_referenceBitmap = m_cachedBuffers->referenceBitmap(); 392 m_referenceBitmap->eraseARGB(0, 0, 0, 0); 393 m_validLayer = true; 394 return; 395 } 396 397 // Create a new cached buffer. 398 if (m_cachedBuffers) 399 delete m_cachedBuffers; 400 m_cachedBuffers = new OwnedBuffers(m_layerSize, true); 401 402 m_layerBuffer = m_cachedBuffers->destBitmap(); 403 m_drawContext = m_cachedBuffers->destBitmap()->context(); 404 m_referenceBitmap = m_cachedBuffers->referenceBitmap(); 405 m_validLayer = true; 406 } 407 408 void TransparencyWin::compositeOpaqueComposite() 409 { 410 if (!m_validLayer) 411 return; 412 413 m_destContext->save(); 414 415 SkBitmap* bitmap = const_cast<SkBitmap*>( 416 &bitmapForContext(*m_layerBuffer->context())); 417 418 // This function will be called for WhiteLayer as well, which we don't want 419 // to change. 420 if (m_layerMode == OpaqueCompositeLayer) { 421 // Fix up our bitmap, making it contain only the pixels which changed 422 // and transparent everywhere else. 423 SkAutoLockPixels sourceLock(*m_referenceBitmap); 424 SkAutoLockPixels lock(*bitmap); 425 for (int y = 0; y < bitmap->height(); y++) { 426 uint32_t* source = m_referenceBitmap->getAddr32(0, y); 427 uint32_t* dest = bitmap->getAddr32(0, y); 428 for (int x = 0; x < bitmap->width(); x++) { 429 // Clear out any pixels that were untouched. 430 if (dest[x] == source[x]) 431 dest[x] = 0; 432 else 433 dest[x] |= (0xFF << SK_A32_SHIFT); 434 } 435 } 436 } else 437 makeLayerOpaque(); 438 439 SkRect destRect; 440 if (m_transformMode != Untransform) { 441 // We want to use Untransformed space. 442 // 443 // Note that we DON'T call m_layerBuffer->image() here. This actually 444 // makes a copy of the image, which is unnecessary and slow. Instead, we 445 // just draw the image from inside the destination context. 446 SkMatrix identity; 447 identity.reset(); 448 m_destContext->setMatrix(identity); 449 450 destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY()); 451 } else 452 destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.maxX(), m_sourceRect.maxY()); 453 454 SkPaint paint; 455 paint.setFilterBitmap(true); 456 paint.setAntiAlias(true); 457 458 // Note that we need to specify the source layer subset, since the bitmap 459 // may have been cached and it could be larger than what we're using. 460 SkRect sourceRect = SkRect::MakeWH( 461 SkIntToScalar(m_layerSize.width()), 462 SkIntToScalar(m_layerSize.height())); 463 m_destContext->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint); 464 m_destContext->restore(); 465 } 466 467 void TransparencyWin::compositeTextComposite() 468 { 469 if (!m_validLayer) 470 return; 471 472 const SkBitmap& bitmap = m_layerBuffer->context()->layerBitmap(GraphicsContext::ReadWrite); 473 SkColor textColor = m_textCompositeColor.rgb(); 474 for (int y = 0; y < m_layerSize.height(); y++) { 475 uint32_t* row = bitmap.getAddr32(0, y); 476 for (int x = 0; x < m_layerSize.width(); x++) { 477 // The alpha is the average of the R, G, and B channels. 478 int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3; 479 480 // Apply that alpha to the text color and write the result. 481 row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha)); 482 } 483 } 484 485 // Now the layer has text with the proper color and opacity. 486 m_destContext->save(); 487 488 // We want to use Untransformed space (see above) 489 SkMatrix identity; 490 identity.reset(); 491 m_destContext->setMatrix(identity); 492 SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY() }; 493 494 // Note that we need to specify the source layer subset, since the bitmap 495 // may have been cached and it could be larger than what we're using. 496 SkRect sourceRect = SkRect::MakeWH( 497 SkIntToScalar(m_layerSize.width()), 498 SkIntToScalar(m_layerSize.height())); 499 m_destContext->drawBitmapRect(bitmap, &sourceRect, destRect, 0); 500 m_destContext->restore(); 501 } 502 503 void TransparencyWin::makeLayerOpaque() 504 { 505 if (!m_validLayer) 506 return; 507 508 SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->layerBitmap(GraphicsContext::ReadWrite)); 509 for (int y = 0; y < m_layerSize.height(); y++) { 510 uint32_t* row = bitmap.getAddr32(0, y); 511 for (int x = 0; x < m_layerSize.width(); x++) 512 row[x] |= 0xFF000000; 513 } 514 } 515 516 } // namespace WebCore 517