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