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