1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include "gfxPattern.h"
7
8 #include "gfxUtils.h"
9 #include "gfxTypes.h"
10 #include "gfxASurface.h"
11 #include "gfxPlatform.h"
12 #include "gfx2DGlue.h"
13 #include "gfxGradientCache.h"
14 #include "mozilla/gfx/2D.h"
15
16 #include "cairo.h"
17
18 #include <vector>
19
20 using namespace mozilla::gfx;
21
gfxPattern(const Color & aColor)22 gfxPattern::gfxPattern(const Color& aColor)
23 : mExtend(ExtendMode::CLAMP)
24 {
25 mGfxPattern.InitColorPattern(ToDeviceColor(aColor));
26 }
27
28 // linear
gfxPattern(gfxFloat x0,gfxFloat y0,gfxFloat x1,gfxFloat y1)29 gfxPattern::gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1)
30 : mExtend(ExtendMode::CLAMP)
31 {
32 mGfxPattern.InitLinearGradientPattern(Point(x0, y0), Point(x1, y1), nullptr);
33 }
34
35 // radial
gfxPattern(gfxFloat cx0,gfxFloat cy0,gfxFloat radius0,gfxFloat cx1,gfxFloat cy1,gfxFloat radius1)36 gfxPattern::gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0,
37 gfxFloat cx1, gfxFloat cy1, gfxFloat radius1)
38 : mExtend(ExtendMode::CLAMP)
39 {
40 mGfxPattern.InitRadialGradientPattern(Point(cx0, cy0), Point(cx1, cy1),
41 radius0, radius1, nullptr);
42 }
43
44 // Azure
gfxPattern(SourceSurface * aSurface,const Matrix & aPatternToUserSpace)45 gfxPattern::gfxPattern(SourceSurface *aSurface, const Matrix &aPatternToUserSpace)
46 : mPatternToUserSpace(aPatternToUserSpace)
47 , mExtend(ExtendMode::CLAMP)
48 {
49 mGfxPattern.InitSurfacePattern(aSurface, mExtend, Matrix(), // matrix is overridden in GetPattern()
50 mozilla::gfx::SamplingFilter::GOOD);
51 }
52
53 void
AddColorStop(gfxFloat offset,const Color & c)54 gfxPattern::AddColorStop(gfxFloat offset, const Color& c)
55 {
56 if (mGfxPattern.GetPattern()->GetType() != PatternType::LINEAR_GRADIENT &&
57 mGfxPattern.GetPattern()->GetType() != PatternType::RADIAL_GRADIENT) {
58 return;
59 }
60
61 mStops = nullptr;
62
63 GradientStop stop;
64 stop.offset = offset;
65 stop.color = ToDeviceColor(c);
66 mStopsList.AppendElement(stop);
67 }
68
69 void
SetColorStops(GradientStops * aStops)70 gfxPattern::SetColorStops(GradientStops* aStops)
71 {
72 mStops = aStops;
73 }
74
75 void
CacheColorStops(const DrawTarget * aDT)76 gfxPattern::CacheColorStops(const DrawTarget *aDT)
77 {
78 mStops = gfxGradientCache::GetOrCreateGradientStops(aDT, mStopsList, mExtend);
79 }
80
81 void
SetMatrix(const gfxMatrix & aPatternToUserSpace)82 gfxPattern::SetMatrix(const gfxMatrix& aPatternToUserSpace)
83 {
84 mPatternToUserSpace = ToMatrix(aPatternToUserSpace);
85 // Cairo-pattern matrices specify the conversion from DrawTarget to pattern
86 // space. Azure pattern matrices specify the conversion from pattern to
87 // DrawTarget space.
88 mPatternToUserSpace.Invert();
89 }
90
91 gfxMatrix
GetMatrix() const92 gfxPattern::GetMatrix() const
93 {
94 // invert at the higher precision of gfxMatrix
95 // cause we need to convert at some point anyways
96 gfxMatrix mat = ThebesMatrix(mPatternToUserSpace);
97 mat.Invert();
98 return mat;
99 }
100
101 gfxMatrix
GetInverseMatrix() const102 gfxPattern::GetInverseMatrix() const
103 {
104 return ThebesMatrix(mPatternToUserSpace);
105 }
106
107 Pattern*
GetPattern(const DrawTarget * aTarget,Matrix * aOriginalUserToDevice)108 gfxPattern::GetPattern(const DrawTarget *aTarget,
109 Matrix *aOriginalUserToDevice)
110 {
111 Matrix patternToUser = mPatternToUserSpace;
112
113 if (aOriginalUserToDevice &&
114 *aOriginalUserToDevice != aTarget->GetTransform()) {
115 // mPatternToUserSpace maps from pattern space to the original user space,
116 // but aTarget now has a transform to a different user space. In order for
117 // the Pattern* that we return to be usable in aTarget's new user space we
118 // need the Pattern's mMatrix to be the transform from pattern space to
119 // aTarget's -new- user space. That transform is equivalent to the
120 // transform from pattern space to original user space (patternToUser),
121 // multiplied by the transform from original user space to device space,
122 // multiplied by the transform from device space to current user space.
123
124 Matrix deviceToCurrentUser = aTarget->GetTransform();
125 deviceToCurrentUser.Invert();
126
127 patternToUser = patternToUser * *aOriginalUserToDevice * deviceToCurrentUser;
128 }
129 patternToUser.NudgeToIntegers();
130
131 if (!mStops &&
132 !mStopsList.IsEmpty()) {
133 mStops = aTarget->CreateGradientStops(mStopsList.Elements(),
134 mStopsList.Length(), mExtend);
135 }
136
137 switch (mGfxPattern.GetPattern()->GetType()) {
138 case PatternType::SURFACE: {
139 SurfacePattern* surfacePattern = static_cast<SurfacePattern*>(mGfxPattern.GetPattern());
140 surfacePattern->mMatrix = patternToUser;
141 surfacePattern->mExtendMode = mExtend;
142 break;
143 }
144 case PatternType::LINEAR_GRADIENT: {
145 LinearGradientPattern* linearGradientPattern = static_cast<LinearGradientPattern*>(mGfxPattern.GetPattern());
146 linearGradientPattern->mMatrix = patternToUser;
147 linearGradientPattern->mStops = mStops;
148 break;
149 }
150 case PatternType::RADIAL_GRADIENT: {
151 RadialGradientPattern* radialGradientPattern = static_cast<RadialGradientPattern*>(mGfxPattern.GetPattern());
152 radialGradientPattern->mMatrix = patternToUser;
153 radialGradientPattern->mStops = mStops;
154 break;
155 }
156 default:
157 /* Reassure the compiler we are handling all the enum values. */
158 break;
159 }
160
161 return mGfxPattern.GetPattern();
162 }
163
164 void
SetExtend(ExtendMode aExtend)165 gfxPattern::SetExtend(ExtendMode aExtend)
166 {
167 mExtend = aExtend;
168 mStops = nullptr;
169 }
170
171 bool
IsOpaque()172 gfxPattern::IsOpaque()
173 {
174 if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) {
175 return false;
176 }
177
178 if (static_cast<SurfacePattern*>(mGfxPattern.GetPattern())->mSurface->GetFormat() == SurfaceFormat::B8G8R8X8) {
179 return true;
180 }
181 return false;
182 }
183
184 void
SetSamplingFilter(gfx::SamplingFilter filter)185 gfxPattern::SetSamplingFilter(gfx::SamplingFilter filter)
186 {
187 if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) {
188 return;
189 }
190
191 static_cast<SurfacePattern*>(mGfxPattern.GetPattern())->mSamplingFilter = filter;
192 }
193
194 SamplingFilter
SamplingFilter() const195 gfxPattern::SamplingFilter() const
196 {
197 if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) {
198 return gfx::SamplingFilter::GOOD;
199 }
200 return static_cast<const SurfacePattern*>(mGfxPattern.GetPattern())->mSamplingFilter;
201 }
202
203 bool
GetSolidColor(Color & aColorOut)204 gfxPattern::GetSolidColor(Color& aColorOut)
205 {
206 if (mGfxPattern.GetPattern()->GetType() == PatternType::COLOR) {
207 aColorOut = static_cast<ColorPattern*>(mGfxPattern.GetPattern())->mColor;
208 return true;
209 }
210
211 return false;
212 }
213
214 int
CairoStatus()215 gfxPattern::CairoStatus()
216 {
217 return CAIRO_STATUS_SUCCESS;
218 }
219