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