1/*
2 * Copyright (C) 2008, 2013 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "DragImage.h"
28
29#include "BitmapInfo.h"
30#include "CachedImage.h"
31#include "GraphicsContext.h"
32#include "GraphicsContextPlatformPrivateCairo.h"
33#include "HWndDC.h"
34#include "Image.h"
35#include <cairo-win32.h>
36#include <windows.h>
37#include <wtf/RetainPtr.h>
38#include <wtf/win/GDIObject.h>
39
40namespace WebCore {
41
42void deallocContext(PlatformContextCairo* target)
43{
44    delete target;
45}
46
47GDIObject<HBITMAP> allocImage(HDC dc, IntSize size, PlatformContextCairo** targetRef)
48{
49    BitmapInfo bmpInfo = BitmapInfo::create(size);
50
51    LPVOID bits;
52    auto hbmp = adoptGDIObject(::CreateDIBSection(dc, &bmpInfo, DIB_RGB_COLORS, &bits, 0, 0));
53
54    // At this point, we have a Cairo surface that points to a Windows DIB.  The DIB interprets
55    // with the opposite meaning of positive Y axis, so everything we draw into this cairo
56    // context is going to be upside down.
57    if (!targetRef)
58        return hbmp;
59
60    cairo_surface_t* bitmapContext = cairo_image_surface_create_for_data((unsigned char*)bits,
61                                               CAIRO_FORMAT_ARGB32,
62                                               bmpInfo.bmiHeader.biWidth,
63                                               bmpInfo.bmiHeader.biHeight,
64                                               bmpInfo.bmiHeader.biWidth * 4);
65
66    if (!bitmapContext)
67        return GDIObject<HBITMAP>();
68
69    cairo_t* cr = cairo_create(bitmapContext);
70    cairo_surface_destroy(bitmapContext);
71
72    // At this point, we have a Cairo surface that points to a Windows DIB.  The DIB interprets
73    // with the opposite meaning of positive Y axis, so everything we draw into this cairo
74    // context is going to be upside down.
75    //
76    // So, we must invert the CTM for the context so that drawing commands will be flipped
77    // before they get written to the internal buffer.
78    cairo_matrix_t matrix;
79    cairo_matrix_init(&matrix, 1.0, 0.0, 0.0, -1.0, 0.0, size.height());
80    cairo_set_matrix(cr, &matrix);
81
82    *targetRef = new PlatformGraphicsContext(cr);
83    cairo_destroy(cr);
84
85    return hbmp;
86}
87
88static cairo_surface_t* createCairoContextFromBitmap(HBITMAP bitmap)
89{
90    BITMAP info;
91    GetObject(bitmap, sizeof(info), &info);
92    ASSERT(info.bmBitsPixel == 32);
93
94    // At this point, we have a Cairo surface that points to a Windows BITMAP.  The BITMAP
95    // has the opposite meaning of positive Y axis, so everything we draw into this cairo
96    // context is going to be upside down.
97    return cairo_image_surface_create_for_data((unsigned char*)info.bmBits,
98                                               CAIRO_FORMAT_ARGB32,
99                                               info.bmWidth,
100                                               info.bmHeight,
101                                               info.bmWidthBytes);
102}
103
104DragImageRef scaleDragImage(DragImageRef imageRef, FloatSize scale)
105{
106    // FIXME: due to the way drag images are done on windows we need
107    // to preprocess the alpha channel <rdar://problem/5015946>
108    if (!imageRef)
109        return 0;
110
111    GDIObject<HBITMAP> hbmp;
112    auto image = adoptGDIObject(imageRef);
113
114    IntSize srcSize = dragImageSize(image.get());
115    IntSize dstSize(static_cast<int>(srcSize.width() * scale.width()), static_cast<int>(srcSize.height() * scale.height()));
116
117    HWndDC dc(0);
118    auto dstDC = adoptGDIObject(::CreateCompatibleDC(dc));
119    if (!dstDC)
120        goto exit;
121
122    PlatformContextCairo* targetContext;
123    hbmp = allocImage(dstDC.get(), dstSize, &targetContext);
124    if (!hbmp)
125        goto exit;
126
127    cairo_surface_t* srcImage = createCairoContextFromBitmap(image.get());
128
129    // Scale the target surface to the new image size, and flip it
130    // so that when we set the srcImage as the surface it will draw
131    // right-side-up.
132    cairo_t* cr = targetContext->cr();
133    cairo_translate(cr, 0, dstSize.height());
134    cairo_scale(cr, scale.width(), -scale.height());
135    cairo_set_source_surface(cr, srcImage, 0.0, 0.0);
136
137    // Now we can paint and get the correct result
138    cairo_paint(cr);
139
140    cairo_surface_destroy(srcImage);
141    deallocContext(targetContext);
142
143exit:
144    if (!hbmp)
145        hbmp.swap(image);
146    return hbmp.leak();
147}
148
149DragImageRef createDragImageFromImage(Image* img, ImageOrientationDescription)
150{
151    HWndDC dc(0);
152    auto workingDC = adoptGDIObject(::CreateCompatibleDC(dc));
153    if (!workingDC)
154        return 0;
155
156    PlatformContextCairo* drawContext = 0;
157    auto hbmp = allocImage(workingDC.get(), IntSize(img->size()), &drawContext);
158    if (!hbmp || !drawContext)
159        return 0;
160
161    cairo_t* cr = drawContext->cr();
162    cairo_set_source_rgb(cr, 1.0, 0.0, 1.0);
163    cairo_fill_preserve(cr);
164
165    RefPtr<cairo_surface_t> surface = img->nativeImageForCurrentFrame();
166    if (surface) {
167        // Draw the image.
168        cairo_set_source_surface(cr, surface.get(), 0.0, 0.0);
169        cairo_paint(cr);
170    }
171
172    deallocContext(drawContext);
173
174    return hbmp.leak();
175}
176
177}
178