Index: third_party/WebKit/WebCore/platform/graphics/chromium/TransparencyWin.cpp |
=================================================================== |
--- third_party/WebKit/WebCore/platform/graphics/chromium/TransparencyWin.cpp (revision 0) |
+++ third_party/WebKit/WebCore/platform/graphics/chromium/TransparencyWin.cpp (revision 0) |
@@ -0,0 +1,421 @@ |
+/* |
+ * Copyright (C) 2009 Google Inc. All rights reserved. |
+ * |
+ * Redistribution and use in source and binary forms, with or without |
+ * modification, are permitted provided that the following conditions are |
+ * met: |
+ * |
+ * * Redistributions of source code must retain the above copyright |
+ * notice, this list of conditions and the following disclaimer. |
+ * * Redistributions in binary form must reproduce the above |
+ * copyright notice, this list of conditions and the following disclaimer |
+ * in the documentation and/or other materials provided with the |
+ * distribution. |
+ * * Neither the name of Google Inc. nor the names of its |
+ * contributors may be used to endorse or promote products derived from |
+ * this software without specific prior written permission. |
+ * |
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ */ |
+ |
+#include "config.h" |
+#include <windows.h> |
+ |
+#include "GraphicsContext.h" |
+#include "ImageBuffer.h" |
+#include "PlatformContextSkia.h" |
+#include "SimpleFontData.h" |
+#include "TransformationMatrix.h" |
+#include "TransparencyWin.h" |
+ |
+#include "SkColorPriv.h" |
+#include "skia/ext/platform_canvas.h" |
+ |
+namespace WebCore { |
+ |
+namespace { |
+ |
+// If either of these pointers is non-NULL, both must be valid and point to |
+// bitmaps of the same size. |
+struct StaticBuffer |
+{ |
+ // The destination bitmap we're drawing into. |
+ ImageBuffer* destBitmap; |
+ |
+ // This could be an ImageBuffer but this is an optimization. Since this is |
+ // only ever used as a reference, we don't need to make a full |
+ // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap |
+ // is much faster since it's just a Malloc rather than a GDI call. |
+ SkBitmap* referenceBitmap; |
+} staticBuffer = {NULL, NULL}; |
+ |
+// The maximum size in pixels of the buffer we'll keep around for drawing text |
+// into. Buffers larger than this will be destroyed when we're done with them. |
+const int maxCachedBufferPixelSize = 65536; |
+ |
+inline skia::PlatformCanvas* canvasForContext(GraphicsContext* context) |
+{ |
+ return context->platformContext()->canvas(); |
+} |
+ |
+inline const SkBitmap& bitmapForContext(GraphicsContext* context) |
+{ |
+ return canvasForContext(context)-> |
+ getTopPlatformDevice().accessBitmap(false); |
+} |
+ |
+void compositeToCopy(GraphicsContext* sourceLayers, |
+ GraphicsContext* destContext, |
+ const TransformationMatrix& matrix) |
+{ |
+ // Make a list of all devices. The iterator goes top-down, and we want |
+ // bottom-up. |
+ struct DeviceInfo { |
+ DeviceInfo(SkDevice* d, int lx, int ly) |
+ : device(d) |
+ , x(lx) |
+ , y(ly) {} |
+ SkDevice* device; |
+ int x, y; |
+ }; |
+ Vector<DeviceInfo> devices; |
+ SkCanvas* sourceCanvas = canvasForContext(sourceLayers); |
+ SkCanvas::LayerIter iter(sourceCanvas, false); |
+ while (!iter.done()) { |
+ devices.append(DeviceInfo(iter.device(), iter.x(), iter.y())); |
+ iter.next(); |
+ } |
+ |
+ // Create a temporary canvas for the compositing into the destination. |
+ SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext)); |
+ SkCanvas destCanvas(*destBmp); |
+ destCanvas.setMatrix(matrix); |
+ |
+ for (int i = devices.size() - 1; i >= 0; i--) { |
+ const SkBitmap& srcBmp = devices[i].device->accessBitmap(false); |
+ |
+ SkRect destRect; |
+ destRect.fLeft = devices[i].x; |
+ destRect.fTop = devices[i].y; |
+ destRect.fRight = destRect.fLeft + srcBmp.width(); |
+ destRect.fBottom = destRect.fTop + srcBmp.height(); |
+ |
+ destCanvas.drawBitmapRect(srcBmp, NULL, destRect); |
+ } |
+} |
+ |
+} // namespace |
+ |
+TransparencyWin::TransparencyWin() |
+ : m_destContext(0) |
+ , m_orgTransform() |
+ , m_layerMode(NoLayer) |
+ , m_transformMode(KeepTransform) |
+ , m_drawContext(0) |
+ , m_savedOnDrawContext(false) |
+ , m_layerBuffer(0) |
+ , m_referenceBitmap(0) |
+{ |
+} |
+ |
+TransparencyWin::~TransparencyWin() |
+{ |
+ // Matches the save() in initializeNewTextContext (or the constructor for |
+ // SCALE) to put the context back into the same state we found it. |
+ if (m_savedOnDrawContext) |
+ m_drawContext->restore(); |
+ |
+ switch (m_layerMode) { |
+ case NoLayer: |
+ break; |
+ case OpaqueCompositeLayer: |
+ case WhiteLayer: |
+ compositeOpaqueComposite(); |
+ break; |
+ case TextComposite: |
+ compositeTextComposite(); |
+ break; |
+ } |
+} |
+ |
+void TransparencyWin::init(GraphicsContext* dest, |
+ LayerMode layerMode, |
+ TransformMode transformMode, |
+ const IntRect& region) { |
+ m_destContext = dest; |
+ m_orgTransform = dest->getCTM(); |
+ m_layerMode = layerMode; |
+ m_transformMode = transformMode; |
+ m_sourceRect = region; |
+ |
+ setupLayer(region); |
+ setupTransform(region); |
+} |
+ |
+void TransparencyWin::setupLayer(const IntRect& region) |
+{ |
+ // Compute the size of the layer we're making. |
+ if (m_transformMode == Untransform) { |
+ // The meaning of the "transformed" source rect is a little ambigous |
+ // here. The rest of the code doesn't care about it in the Untransform |
+ // case since we're using our own happy coordinate system. So we set it |
+ // to be the source rect since that matches how the code below actually |
+ // uses the variable: to determine how to translate things to account |
+ // for the offset of the layer. |
+ m_transformedSourceRect = m_sourceRect; |
+ m_layerSize = IntSize(m_sourceRect.width(), m_sourceRect.height()); |
+ } else { |
+ m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect); |
+ m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height()); |
+ } |
+ |
+ switch (m_layerMode) { |
+ case NoLayer: |
+ m_drawContext = m_destContext; // Draw to the source context. |
+ break; |
+ case OpaqueCompositeLayer: { |
+ initializeNewContext(); |
+ |
+ TransformationMatrix mapping; |
+ mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); |
+ if (m_transformMode == Untransform){ |
+ // Compute the inverse mapping from the canvas space to the |
+ // coordinate space of our bitmap. |
+ mapping = m_orgTransform.inverse() * mapping; |
+ } |
+ compositeToCopy(m_destContext, m_drawContext, mapping); |
+ |
+ // Save the reference layer so we can tell what changed. |
+ SkCanvas referenceCanvas(*m_referenceBitmap); |
+ referenceCanvas.drawBitmap(bitmapForContext(m_drawContext), 0, 0); |
+ // Layer rect represents the part of the original layer. |
+ break; |
+ } |
+ case TextComposite: |
+ ASSERT(m_transformMode == KeepTransform); |
+ // Fall through to filling with white. |
+ case WhiteLayer: |
+ initializeNewContext(); |
+ m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white); |
+ // Layer rect represents the part of the original layer. |
+ break; |
+ } |
+} |
+ |
+void TransparencyWin::setupTransform(const IntRect& region) |
+{ |
+ switch (m_transformMode) { |
+ case KeepTransform: |
+ if (m_layerMode != NoLayer) { |
+ // Need to save things since we're modifying the transform. |
+ m_drawContext->save(); |
+ m_savedOnDrawContext = true; |
+ |
+ // Account for the fact that the layer may be offset from the |
+ // original. This only happens when we create a layer that has the |
+ // same coordinate space as the parent. |
+ TransformationMatrix xform; |
+ xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); |
+ |
+ // We're making a layer, so apply the old transform to the new one |
+ // so it's maintained. We know the new layer has the identity |
+ // transform now, we we can just multiply it. |
+ xform = m_orgTransform * xform; |
+ m_drawContext->concatCTM(xform); |
+ } |
+ m_drawRect = m_sourceRect; |
+ break; |
+ case Untransform: |
+ ASSERT(m_layerMode != NoLayer); |
+ // We now have a new layer with the identity transform, which is the |
+ // Untransformed space we'll use for drawing. |
+ m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); |
+ break; |
+ case ScaleTransform: |
+ if (m_layerMode == NoLayer) { |
+ // Need to save things since we're modifying the layer. |
+ m_drawContext->save(); |
+ m_savedOnDrawContext = true; |
+ |
+ // Undo the transform on the current layer when we're re-using the |
+ // current one. |
+ m_drawContext->concatCTM(m_drawContext->getCTM().inverse()); |
+ |
+ // We're drawing to the original layer with just a different size. |
+ m_drawRect = m_transformedSourceRect; |
+ } else { |
+ // Just go ahead and use the layer's coordinate space to draw into. |
+ // It will have the scaled size, and an identity transform loaded. |
+ m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); |
+ } |
+ break; |
+ } |
+} |
+ |
+void TransparencyWin::setTextCompositeColor(Color color) |
+{ |
+ m_textCompositeColor = color; |
+} |
+ |
+void TransparencyWin::initializeNewContext() |
+{ |
+ int pixelSize = m_layerSize.width() * m_layerSize.height(); |
+ if (pixelSize > maxCachedBufferPixelSize) { |
+ // Create a 1-off buffer for drawing into. |
+ m_ownedBuffer.adopt(ImageBuffer::create(m_layerSize, false)); |
+ m_layerBuffer = m_ownedBuffer.get(); |
+ |
+ if (m_layerMode == OpaqueCompositeLayer) { |
+ // Only create the reference buffer if needed. Below, we always |
+ // create the buffer to reduce complexity when using the cached |
+ // bitmap by keeping everything in sync. |
+ m_ownedReferenceBuffer.set(new SkBitmap); |
+ m_ownedReferenceBuffer->setConfig(SkBitmap::kARGB_8888_Config, |
+ m_layerSize.width(), |
+ m_layerSize.height()); |
+ m_ownedReferenceBuffer->allocPixels(); |
+ m_referenceBitmap = m_ownedReferenceBuffer.get(); |
+ } |
+ |
+ m_drawContext = m_ownedBuffer->context(); |
+ return; |
+ } |
+ |
+ if (staticBuffer.destBitmap && |
+ staticBuffer.destBitmap->size().width() >= m_layerSize.width() && |
+ staticBuffer.destBitmap->size().height() >= m_layerSize.height()) { |
+ // We can re-use the existing buffer. We don't need to clear it since |
+ // all layer modes will clear it in their initialization. |
+ m_drawContext = staticBuffer.destBitmap->context(); |
+ m_layerBuffer = staticBuffer.destBitmap; |
+ |
+ m_referenceBitmap = staticBuffer.referenceBitmap; |
+ return; |
+ } |
+ |
+ // Create a new cached buffer. |
+ delete staticBuffer.destBitmap; |
+ delete staticBuffer.referenceBitmap; |
+ |
+ staticBuffer.destBitmap = ImageBuffer::create(m_layerSize, false).release(); |
+ staticBuffer.referenceBitmap = new SkBitmap; |
+ staticBuffer.referenceBitmap->setConfig(SkBitmap::kARGB_8888_Config, m_layerSize.width(), m_layerSize.height()); |
+ staticBuffer.referenceBitmap->allocPixels(); |
+ |
+ m_layerBuffer = staticBuffer.destBitmap; |
+ m_referenceBitmap = staticBuffer.referenceBitmap; |
+ m_drawContext = staticBuffer.destBitmap->context(); |
+} |
+ |
+void TransparencyWin::compositeOpaqueComposite() |
+{ |
+ SkCanvas* destCanvas = canvasForContext(m_destContext); |
+ destCanvas->save(); |
+ |
+ SkBitmap* bitmap = const_cast<SkBitmap*>( |
+ &bitmapForContext(m_layerBuffer->context())); |
+ |
+ // This function will be called for WhiteLayer as well, which we don't want |
+ // to change. |
+ if (m_layerMode == OpaqueCompositeLayer) { |
+ // Fix up our bitmap, making it contain only the pixels which changed |
+ // and transparent everywhere else. |
+ SkAutoLockPixels sourceLock(*m_referenceBitmap); |
+ SkAutoLockPixels lock(*bitmap); |
+ for (int y = 0; y < bitmap->height(); y++) { |
+ uint32_t* source = m_referenceBitmap->getAddr32(0, y); |
+ uint32_t* dest = bitmap->getAddr32(0, y); |
+ for (int x = 0; x < bitmap->width(); x++) { |
+ // Clear out any pixels that were untouched. |
+ if (dest[x] == source[x]) |
+ dest[x] = 0; |
+ else |
+ dest[x] |= (0xFF << SK_A32_SHIFT); |
+ } |
+ } |
+ } else |
+ makeLayerOpaque(); |
+ |
+ SkRect destRect; |
+ if (m_transformMode != Untransform) { |
+ // We want to use Untransformed space. |
+ // |
+ // Note that we DON'T call m_layerBuffer->image() here. This actually |
+ // makes a copy of the image, which is unnecessary and slow. Instead, we |
+ // just draw the image from inside the destination context. |
+ SkMatrix identity; |
+ identity.reset(); |
+ destCanvas->setMatrix(identity); |
+ |
+ destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom()); |
+ } else { |
+ destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.right(), m_sourceRect.bottom()); |
+ } |
+ |
+ SkPaint paint; |
+ paint.setFilterBitmap(true); |
+ paint.setAntiAlias(true); |
+ |
+ // Note that we need to specify the source layer subset, since the bitmap |
+ // may have been cached and it could be larger than what we're using. |
+ SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; |
+ destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint); |
+ destCanvas->restore(); |
+} |
+ |
+void TransparencyWin::compositeTextComposite() |
+{ |
+ const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()-> |
+ canvas()->getTopPlatformDevice().accessBitmap(true); |
+ SkColor textColor = m_textCompositeColor.rgb(); |
+ for (int y = 0; y < m_layerSize.height(); y++) { |
+ uint32_t* row = bitmap.getAddr32(0, y); |
+ for (int x = 0; x < m_layerSize.width(); x++) { |
+ // The alpha is the average of the R, G, and B channels. |
+ int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + |
+ SkGetPackedB32(row[x])) / 3; |
+ |
+ // Apply that alpha to the text color and write the result. |
+ row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha)); |
+ } |
+ } |
+ |
+ // Now the layer has text with the proper color and opacity. |
+ SkCanvas* destCanvas = canvasForContext(m_destContext); |
+ |
+ // We want to use Untransformed space (see above) |
+ SkMatrix identity; |
+ identity.reset(); |
+ destCanvas->setMatrix(identity); |
+ SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom() }; |
+ |
+ // Note that we need to specify the source layer subset, since the bitmap |
+ // may have been cached and it could be larger than what we're using. |
+ SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; |
+ destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, NULL); |
+ destCanvas->restore(); |
+} |
+ |
+void TransparencyWin::makeLayerOpaque() |
+{ |
+ SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()-> |
+ canvas()->getTopPlatformDevice().accessBitmap(true)); |
+ for (int y = 0; y < m_layerSize.height(); y++) { |
+ uint32_t* row = bitmap.getAddr32(0, y); |
+ for (int x = 0; x < m_layerSize.width(); x++) |
+ row[x] |= 0xFF000000; |
+ } |
+} |
+ |
+} // namespace WebCore |
+ |
Property changes on: third_party\WebKit\WebCore\platform\graphics\chromium\TransparencyWin.cpp |
___________________________________________________________________ |
Added: svn:mergeinfo |