JavaScriptCore:
[WebKit-https.git] / WebCore / html / CanvasPattern.cpp
1 /*
2  * Copyright (C) 2006 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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 "CanvasPattern.h"
28
29 #include "CachedImage.h"
30 #include "ExceptionCode.h"
31 #include "FloatRect.h"
32 #include "GraphicsContext.h"
33 #include "Image.h"
34
35 namespace WebCore {
36
37 void CanvasPattern::parseRepetitionType(const String& type, bool& repeatX, bool& repeatY, ExceptionCode& ec)
38 {
39     if (type.isEmpty() || type == "repeat") {
40         repeatX = true;
41         repeatY = true;
42         ec = 0;
43         return;
44     }
45     if (type == "no-repeat") {
46         repeatX = false;
47         repeatY = false;
48         ec = 0;
49         return;
50     }
51     if (type == "repeat-x") {
52         repeatX = true;
53         repeatY = false;
54         ec = 0;
55         return;
56     }
57     if (type == "repeat-y") {
58         repeatX = false;
59         repeatY = true;
60         ec = 0;
61         return;
62     }
63     ec = SYNTAX_ERR;
64 }
65
66 #if PLATFORM(CG)
67
68 CanvasPattern::CanvasPattern(CGImageRef image, bool repeatX, bool repeatY)
69     : RefCounted<CanvasPattern>(0)
70     , m_platformImage(image)
71     , m_cachedImage(0)
72     , m_repeatX(repeatX)
73     , m_repeatY(repeatY)
74 {
75 }
76
77 #elif PLATFORM(CAIRO)
78
79 CanvasPattern::CanvasPattern(cairo_surface_t* surface, bool repeatX, bool repeatY)
80     : RefCounted<CanvasPattern>(0)
81     , m_platformImage(cairo_surface_reference(surface))
82     , m_cachedImage(0)
83     , m_repeatX(repeatX)
84     , m_repeatY(repeatY)
85 {
86 }
87
88 #endif
89
90 CanvasPattern::CanvasPattern(CachedImage* cachedImage, bool repeatX, bool repeatY)
91     : RefCounted<CanvasPattern>(0)
92 #if PLATFORM(CG) || PLATFORM(CAIRO)
93     , m_platformImage(0)
94 #endif
95     , m_cachedImage(cachedImage)
96     , m_repeatX(repeatX)
97     , m_repeatY(repeatY)
98 {
99     if (cachedImage)
100         cachedImage->ref(this);
101 }
102
103 CanvasPattern::~CanvasPattern()
104 {
105 #if PLATFORM(CAIRO)
106     if (m_platformImage)
107         cairo_surface_destroy(m_platformImage);
108 #endif
109     if (m_cachedImage)
110         m_cachedImage->deref(this);
111 }
112
113 #if PLATFORM(CG)
114
115 static void patternCallback(void* info, CGContextRef context)
116 {
117     CGImageRef platformImage = static_cast<CanvasPattern*>(info)->platformImage();
118     if (platformImage) {
119         CGRect rect = GraphicsContext(context).roundToDevicePixels(
120             FloatRect(0, 0, CGImageGetWidth(platformImage), CGImageGetHeight(platformImage)));
121         CGContextDrawImage(context, rect, platformImage);
122         return;
123     }
124
125     CachedImage* cachedImage = static_cast<CanvasPattern*>(info)->cachedImage();
126     if (!cachedImage)
127         return;
128     Image* image = cachedImage->image();
129     if (!image)
130         return;
131
132     FloatRect rect = GraphicsContext(context).roundToDevicePixels(image->rect());
133
134     if (image->getCGImageRef()) {
135         CGContextDrawImage(context, rect, image->getCGImageRef());
136         // FIXME: We should refactor this code to use the platform-independent 
137         // drawing API in all cases. Then, this didDraw call will happen 
138         // automatically, and we can remove it.
139         cachedImage->didDraw(image);
140         return;
141     }
142
143     GraphicsContext(context).drawImage(image, rect);
144 }
145
146 static void patternReleaseCallback(void* info)
147 {
148     static_cast<CanvasPattern*>(info)->deref();
149 }
150
151 CGPatternRef CanvasPattern::createPattern(const CGAffineTransform& transform)
152 {
153     CGRect rect;
154     rect.origin.x = 0;
155     rect.origin.y = 0;
156     if (m_platformImage) {
157         rect.size.width = CGImageGetWidth(m_platformImage.get());
158         rect.size.height = CGImageGetHeight(m_platformImage.get());
159     } else {
160         if (!m_cachedImage)
161             return 0;
162         Image* image = m_cachedImage->image();
163         if (!image)
164             return 0;
165         rect.size.width = image->width();
166         rect.size.height = image->height();
167     }
168
169     CGAffineTransform patternTransform =
170         CGAffineTransformTranslate(CGAffineTransformScale(transform, 1, -1), 0, -rect.size.height);
171
172     float xStep = m_repeatX ? rect.size.width : FLT_MAX;
173     // If FLT_MAX should also be used for yStep, nothing is rendered. Using fractions of FLT_MAX also
174     // result in nothing being rendered. This is not a problem with xStep.
175     // INT_MAX is almost correct, but there seems to be some number wrapping occuring making the fill
176     // pattern is not filled correctly. 
177     // So, just pick a really large number that works. 
178     float yStep = m_repeatY ? rect.size.height : (100000000.0f);
179
180     const CGPatternCallbacks patternCallbacks = { 0, patternCallback, patternReleaseCallback };
181     ref();
182     return CGPatternCreate(this, rect, patternTransform, xStep, yStep,
183         kCGPatternTilingConstantSpacing, TRUE, &patternCallbacks);
184 }
185
186 #elif PLATFORM(CAIRO)
187
188 cairo_pattern_t* CanvasPattern::createPattern(const cairo_matrix_t& m)
189 {
190     cairo_surface_t* surface = 0;
191     if (m_platformImage) {
192         surface = m_platformImage;
193     } else {
194         if (!m_cachedImage)
195             return 0;
196         Image* image = m_cachedImage->image();
197         if (!image)
198             return 0;
199         surface = image->nativeImageForCurrentFrame();
200     }
201
202     if (!surface)
203         return 0;
204
205     cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface);
206     cairo_pattern_set_matrix(pattern, &m);
207     if (m_repeatX || m_repeatY)
208         cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT);
209     return pattern;
210 }
211
212 #endif
213
214 }