OLD | NEW |
(Empty) | |
| 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 "config.h" |
| 32 #include <windows.h> |
| 33 |
| 34 #include "GraphicsContext.h" |
| 35 #include "ImageBuffer.h" |
| 36 #include "PlatformContextSkia.h" |
| 37 #include "SimpleFontData.h" |
| 38 #include "TransformationMatrix.h" |
| 39 #include "TransparencyWin.h" |
| 40 |
| 41 #include "SkColorPriv.h" |
| 42 #include "skia/ext/platform_canvas.h" |
| 43 |
| 44 namespace WebCore { |
| 45 |
| 46 namespace { |
| 47 |
| 48 // If either of these pointers is non-NULL, both must be valid and point to |
| 49 // bitmaps of the same size. |
| 50 struct StaticBuffer |
| 51 { |
| 52 // The destination bitmap we're drawing into. |
| 53 ImageBuffer* destBitmap; |
| 54 |
| 55 // This could be an ImageBuffer but this is an optimization. Since this is |
| 56 // only ever used as a reference, we don't need to make a full |
| 57 // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap |
| 58 // is much faster since it's just a Malloc rather than a GDI call. |
| 59 SkBitmap* referenceBitmap; |
| 60 } staticBuffer = {NULL, NULL}; |
| 61 |
| 62 // The maximum size in pixels of the buffer we'll keep around for drawing text |
| 63 // into. Buffers larger than this will be destroyed when we're done with them. |
| 64 const int maxCachedBufferPixelSize = 65536; |
| 65 |
| 66 inline skia::PlatformCanvas* canvasForContext(GraphicsContext* context) |
| 67 { |
| 68 return context->platformContext()->canvas(); |
| 69 } |
| 70 |
| 71 inline const SkBitmap& bitmapForContext(GraphicsContext* context) |
| 72 { |
| 73 return canvasForContext(context)-> |
| 74 getTopPlatformDevice().accessBitmap(false); |
| 75 } |
| 76 |
| 77 void compositeToCopy(GraphicsContext* sourceLayers, |
| 78 GraphicsContext* destContext, |
| 79 const TransformationMatrix& matrix) |
| 80 { |
| 81 // Make a list of all devices. The iterator goes top-down, and we want |
| 82 // bottom-up. |
| 83 struct DeviceInfo { |
| 84 DeviceInfo(SkDevice* d, int lx, int ly) |
| 85 : device(d) |
| 86 , x(lx) |
| 87 , y(ly) {} |
| 88 SkDevice* device; |
| 89 int x, y; |
| 90 }; |
| 91 Vector<DeviceInfo> devices; |
| 92 SkCanvas* sourceCanvas = canvasForContext(sourceLayers); |
| 93 SkCanvas::LayerIter iter(sourceCanvas, false); |
| 94 while (!iter.done()) { |
| 95 devices.append(DeviceInfo(iter.device(), iter.x(), iter.y())); |
| 96 iter.next(); |
| 97 } |
| 98 |
| 99 // Create a temporary canvas for the compositing into the destination. |
| 100 SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext)); |
| 101 SkCanvas destCanvas(*destBmp); |
| 102 destCanvas.setMatrix(matrix); |
| 103 |
| 104 for (int i = devices.size() - 1; i >= 0; i--) { |
| 105 const SkBitmap& srcBmp = devices[i].device->accessBitmap(false); |
| 106 |
| 107 SkRect destRect; |
| 108 destRect.fLeft = devices[i].x; |
| 109 destRect.fTop = devices[i].y; |
| 110 destRect.fRight = destRect.fLeft + srcBmp.width(); |
| 111 destRect.fBottom = destRect.fTop + srcBmp.height(); |
| 112 |
| 113 destCanvas.drawBitmapRect(srcBmp, NULL, destRect); |
| 114 } |
| 115 } |
| 116 |
| 117 } // namespace |
| 118 |
| 119 TransparencyWin::TransparencyWin() |
| 120 : m_destContext(0) |
| 121 , m_orgTransform() |
| 122 , m_layerMode(NoLayer) |
| 123 , m_transformMode(KeepTransform) |
| 124 , m_drawContext(0) |
| 125 , m_savedOnDrawContext(false) |
| 126 , m_layerBuffer(0) |
| 127 , m_referenceBitmap(0) |
| 128 { |
| 129 } |
| 130 |
| 131 TransparencyWin::~TransparencyWin() |
| 132 { |
| 133 // Matches the save() in initializeNewTextContext (or the constructor for |
| 134 // SCALE) to put the context back into the same state we found it. |
| 135 if (m_savedOnDrawContext) |
| 136 m_drawContext->restore(); |
| 137 |
| 138 switch (m_layerMode) { |
| 139 case NoLayer: |
| 140 break; |
| 141 case OpaqueCompositeLayer: |
| 142 case WhiteLayer: |
| 143 compositeOpaqueComposite(); |
| 144 break; |
| 145 case TextComposite: |
| 146 compositeTextComposite(); |
| 147 break; |
| 148 } |
| 149 } |
| 150 |
| 151 void TransparencyWin::init(GraphicsContext* dest, |
| 152 LayerMode layerMode, |
| 153 TransformMode transformMode, |
| 154 const IntRect& region) { |
| 155 m_destContext = dest; |
| 156 m_orgTransform = dest->getCTM(); |
| 157 m_layerMode = layerMode; |
| 158 m_transformMode = transformMode; |
| 159 m_sourceRect = region; |
| 160 |
| 161 setupLayer(region); |
| 162 setupTransform(region); |
| 163 } |
| 164 |
| 165 void TransparencyWin::setupLayer(const IntRect& region) |
| 166 { |
| 167 // Compute the size of the layer we're making. |
| 168 if (m_transformMode == Untransform) { |
| 169 // The meaning of the "transformed" source rect is a little ambigous |
| 170 // here. The rest of the code doesn't care about it in the Untransform |
| 171 // case since we're using our own happy coordinate system. So we set it |
| 172 // to be the source rect since that matches how the code below actually |
| 173 // uses the variable: to determine how to translate things to account |
| 174 // for the offset of the layer. |
| 175 m_transformedSourceRect = m_sourceRect; |
| 176 m_layerSize = IntSize(m_sourceRect.width(), m_sourceRect.height()); |
| 177 } else { |
| 178 m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect); |
| 179 m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSour
ceRect.height()); |
| 180 } |
| 181 |
| 182 switch (m_layerMode) { |
| 183 case NoLayer: |
| 184 m_drawContext = m_destContext; // Draw to the source context. |
| 185 break; |
| 186 case OpaqueCompositeLayer: { |
| 187 initializeNewContext(); |
| 188 |
| 189 TransformationMatrix mapping; |
| 190 mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect
.y()); |
| 191 if (m_transformMode == Untransform){ |
| 192 // Compute the inverse mapping from the canvas space to the |
| 193 // coordinate space of our bitmap. |
| 194 mapping = m_orgTransform.inverse() * mapping; |
| 195 } |
| 196 compositeToCopy(m_destContext, m_drawContext, mapping); |
| 197 |
| 198 // Save the reference layer so we can tell what changed. |
| 199 SkCanvas referenceCanvas(*m_referenceBitmap); |
| 200 referenceCanvas.drawBitmap(bitmapForContext(m_drawContext), 0, 0); |
| 201 // Layer rect represents the part of the original layer. |
| 202 break; |
| 203 } |
| 204 case TextComposite: |
| 205 ASSERT(m_transformMode == KeepTransform); |
| 206 // Fall through to filling with white. |
| 207 case WhiteLayer: |
| 208 initializeNewContext(); |
| 209 m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::whi
te); |
| 210 // Layer rect represents the part of the original layer. |
| 211 break; |
| 212 } |
| 213 } |
| 214 |
| 215 void TransparencyWin::setupTransform(const IntRect& region) |
| 216 { |
| 217 switch (m_transformMode) { |
| 218 case KeepTransform: |
| 219 if (m_layerMode != NoLayer) { |
| 220 // Need to save things since we're modifying the transform. |
| 221 m_drawContext->save(); |
| 222 m_savedOnDrawContext = true; |
| 223 |
| 224 // Account for the fact that the layer may be offset from the |
| 225 // original. This only happens when we create a layer that has the |
| 226 // same coordinate space as the parent. |
| 227 TransformationMatrix xform; |
| 228 xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRe
ct.y()); |
| 229 |
| 230 // We're making a layer, so apply the old transform to the new one |
| 231 // so it's maintained. We know the new layer has the identity |
| 232 // transform now, we we can just multiply it. |
| 233 xform = m_orgTransform * xform; |
| 234 m_drawContext->concatCTM(xform); |
| 235 } |
| 236 m_drawRect = m_sourceRect; |
| 237 break; |
| 238 case Untransform: |
| 239 ASSERT(m_layerMode != NoLayer); |
| 240 // We now have a new layer with the identity transform, which is the |
| 241 // Untransformed space we'll use for drawing. |
| 242 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); |
| 243 break; |
| 244 case ScaleTransform: |
| 245 if (m_layerMode == NoLayer) { |
| 246 // Need to save things since we're modifying the layer. |
| 247 m_drawContext->save(); |
| 248 m_savedOnDrawContext = true; |
| 249 |
| 250 // Undo the transform on the current layer when we're re-using the |
| 251 // current one. |
| 252 m_drawContext->concatCTM(m_drawContext->getCTM().inverse()); |
| 253 |
| 254 // We're drawing to the original layer with just a different size. |
| 255 m_drawRect = m_transformedSourceRect; |
| 256 } else { |
| 257 // Just go ahead and use the layer's coordinate space to draw into. |
| 258 // It will have the scaled size, and an identity transform loaded. |
| 259 m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); |
| 260 } |
| 261 break; |
| 262 } |
| 263 } |
| 264 |
| 265 void TransparencyWin::setTextCompositeColor(Color color) |
| 266 { |
| 267 m_textCompositeColor = color; |
| 268 } |
| 269 |
| 270 void TransparencyWin::initializeNewContext() |
| 271 { |
| 272 int pixelSize = m_layerSize.width() * m_layerSize.height(); |
| 273 if (pixelSize > maxCachedBufferPixelSize) { |
| 274 // Create a 1-off buffer for drawing into. |
| 275 m_ownedBuffer.adopt(ImageBuffer::create(m_layerSize, false)); |
| 276 m_layerBuffer = m_ownedBuffer.get(); |
| 277 |
| 278 if (m_layerMode == OpaqueCompositeLayer) { |
| 279 // Only create the reference buffer if needed. Below, we always |
| 280 // create the buffer to reduce complexity when using the cached |
| 281 // bitmap by keeping everything in sync. |
| 282 m_ownedReferenceBuffer.set(new SkBitmap); |
| 283 m_ownedReferenceBuffer->setConfig(SkBitmap::kARGB_8888_Config, |
| 284 m_layerSize.width(), |
| 285 m_layerSize.height()); |
| 286 m_ownedReferenceBuffer->allocPixels(); |
| 287 m_referenceBitmap = m_ownedReferenceBuffer.get(); |
| 288 } |
| 289 |
| 290 m_drawContext = m_ownedBuffer->context(); |
| 291 return; |
| 292 } |
| 293 |
| 294 if (staticBuffer.destBitmap && |
| 295 staticBuffer.destBitmap->size().width() >= m_layerSize.width() && |
| 296 staticBuffer.destBitmap->size().height() >= m_layerSize.height()) { |
| 297 // We can re-use the existing buffer. We don't need to clear it since |
| 298 // all layer modes will clear it in their initialization. |
| 299 m_drawContext = staticBuffer.destBitmap->context(); |
| 300 m_layerBuffer = staticBuffer.destBitmap; |
| 301 |
| 302 m_referenceBitmap = staticBuffer.referenceBitmap; |
| 303 return; |
| 304 } |
| 305 |
| 306 // Create a new cached buffer. |
| 307 delete staticBuffer.destBitmap; |
| 308 delete staticBuffer.referenceBitmap; |
| 309 |
| 310 staticBuffer.destBitmap = ImageBuffer::create(m_layerSize, false).release(); |
| 311 staticBuffer.referenceBitmap = new SkBitmap; |
| 312 staticBuffer.referenceBitmap->setConfig(SkBitmap::kARGB_8888_Config, m_layer
Size.width(), m_layerSize.height()); |
| 313 staticBuffer.referenceBitmap->allocPixels(); |
| 314 |
| 315 m_layerBuffer = staticBuffer.destBitmap; |
| 316 m_referenceBitmap = staticBuffer.referenceBitmap; |
| 317 m_drawContext = staticBuffer.destBitmap->context(); |
| 318 } |
| 319 |
| 320 void TransparencyWin::compositeOpaqueComposite() |
| 321 { |
| 322 SkCanvas* destCanvas = canvasForContext(m_destContext); |
| 323 destCanvas->save(); |
| 324 |
| 325 SkBitmap* bitmap = const_cast<SkBitmap*>( |
| 326 &bitmapForContext(m_layerBuffer->context())); |
| 327 |
| 328 // This function will be called for WhiteLayer as well, which we don't want |
| 329 // to change. |
| 330 if (m_layerMode == OpaqueCompositeLayer) { |
| 331 // Fix up our bitmap, making it contain only the pixels which changed |
| 332 // and transparent everywhere else. |
| 333 SkAutoLockPixels sourceLock(*m_referenceBitmap); |
| 334 SkAutoLockPixels lock(*bitmap); |
| 335 for (int y = 0; y < bitmap->height(); y++) { |
| 336 uint32_t* source = m_referenceBitmap->getAddr32(0, y); |
| 337 uint32_t* dest = bitmap->getAddr32(0, y); |
| 338 for (int x = 0; x < bitmap->width(); x++) { |
| 339 // Clear out any pixels that were untouched. |
| 340 if (dest[x] == source[x]) |
| 341 dest[x] = 0; |
| 342 else |
| 343 dest[x] |= (0xFF << SK_A32_SHIFT); |
| 344 } |
| 345 } |
| 346 } else |
| 347 makeLayerOpaque(); |
| 348 |
| 349 SkRect destRect; |
| 350 if (m_transformMode != Untransform) { |
| 351 // We want to use Untransformed space. |
| 352 // |
| 353 // Note that we DON'T call m_layerBuffer->image() here. This actually |
| 354 // makes a copy of the image, which is unnecessary and slow. Instead, we |
| 355 // just draw the image from inside the destination context. |
| 356 SkMatrix identity; |
| 357 identity.reset(); |
| 358 destCanvas->setMatrix(identity); |
| 359 |
| 360 destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m
_transformedSourceRect.right(), m_transformedSourceRect.bottom()); |
| 361 } else { |
| 362 destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.right(), m
_sourceRect.bottom()); |
| 363 } |
| 364 |
| 365 SkPaint paint; |
| 366 paint.setFilterBitmap(true); |
| 367 paint.setAntiAlias(true); |
| 368 |
| 369 // Note that we need to specify the source layer subset, since the bitmap |
| 370 // may have been cached and it could be larger than what we're using. |
| 371 SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; |
| 372 destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint); |
| 373 destCanvas->restore(); |
| 374 } |
| 375 |
| 376 void TransparencyWin::compositeTextComposite() |
| 377 { |
| 378 const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()-> |
| 379 canvas()->getTopPlatformDevice().accessBitmap(true); |
| 380 SkColor textColor = m_textCompositeColor.rgb(); |
| 381 for (int y = 0; y < m_layerSize.height(); y++) { |
| 382 uint32_t* row = bitmap.getAddr32(0, y); |
| 383 for (int x = 0; x < m_layerSize.width(); x++) { |
| 384 // The alpha is the average of the R, G, and B channels. |
| 385 int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + |
| 386 SkGetPackedB32(row[x])) / 3; |
| 387 |
| 388 // Apply that alpha to the text color and write the result. |
| 389 row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha)); |
| 390 } |
| 391 } |
| 392 |
| 393 // Now the layer has text with the proper color and opacity. |
| 394 SkCanvas* destCanvas = canvasForContext(m_destContext); |
| 395 |
| 396 // We want to use Untransformed space (see above) |
| 397 SkMatrix identity; |
| 398 identity.reset(); |
| 399 destCanvas->setMatrix(identity); |
| 400 SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y()
, m_transformedSourceRect.right(), m_transformedSourceRect.bottom() }; |
| 401 |
| 402 // Note that we need to specify the source layer subset, since the bitmap |
| 403 // may have been cached and it could be larger than what we're using. |
| 404 SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; |
| 405 destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, NULL); |
| 406 destCanvas->restore(); |
| 407 } |
| 408 |
| 409 void TransparencyWin::makeLayerOpaque() |
| 410 { |
| 411 SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()-> |
| 412 canvas()->getTopPlatformDevice().accessBitmap(true)); |
| 413 for (int y = 0; y < m_layerSize.height(); y++) { |
| 414 uint32_t* row = bitmap.getAddr32(0, y); |
| 415 for (int x = 0; x < m_layerSize.width(); x++) |
| 416 row[x] |= 0xFF000000; |
| 417 } |
| 418 } |
| 419 |
| 420 } // namespace WebCore |
| 421 |
OLD | NEW |