1 // FillStyle.cpp: Graphical region filling styles, for Gnash.
3 // Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "FillStyle.h"
23 #include <boost/variant.hpp>
24 #include <boost/optional.hpp>
26 #include "smart_ptr.h"
28 #include "SWFStream.h"
29 #include "movie_definition.h"
31 #include "GnashException.h"
32 #include "GnashNumeric.h"
34 #include "RunResources.h"
35 #include "GnashImage.h"
40 // Forward declarations
42 OptionalFillPair
readSolidFill(SWFStream
& in
, SWF::TagType t
,
44 OptionalFillPair
readBitmapFill(SWFStream
& in
, SWF::FillType type
,
45 movie_definition
& md
, bool readMorph
);
46 GradientRecord
readGradientRecord(SWFStream
& in
, SWF::TagType tag
);
51 /// Create a lerped version of two other FillStyles.
53 /// The two fill styles must have exactly the same types. Callers are
54 /// responsible for ensuring this.
55 class SetLerp
: public boost::static_visitor
<>
58 SetLerp(const FillStyle::Fill
& a
, const FillStyle::Fill
& b
, double ratio
)
66 template<typename T
> void operator()(T
& f
) const {
67 const T
& a
= boost::get
<T
>(_a
);
68 const T
& b
= boost::get
<T
>(_b
);
69 f
.setLerp(a
, b
, _ratio
);
73 const FillStyle::Fill
& _a
;
74 const FillStyle::Fill
& _b
;
82 gradientMatrix(GradientFill::Type t
, const SWFMatrix
& m
)
86 case GradientFill::LINEAR
:
87 base
.set_translation(128, 0);
88 base
.set_scale(1.0 / 128, 1.0 / 128);
90 case GradientFill::RADIAL
:
91 base
.set_translation(32, 32);
92 base
.set_scale(1.0 / 512, 1.0 / 512);
99 GradientFill::GradientFill(Type t
, const SWFMatrix
& m
,
100 const GradientRecords
& recs
)
103 interpolation(SWF::GRADIENT_INTERPOLATION_NORMAL
),
107 _matrix(gradientMatrix(t
, m
))
109 assert(recs
.empty() || recs
.size() > 1);
113 GradientFill::setFocalPoint(double d
)
115 _focalPoint
= clamp
<float>(d
, -1, 1);
118 BitmapFill::BitmapFill(SWF::FillType t
, movie_definition
* md
,
119 boost::uint16_t id
, const SWFMatrix
& m
)
130 _smoothingPolicy
= md
->get_version() >= 8 ?
131 BitmapFill::SMOOTHING_ON
: BitmapFill::SMOOTHING_UNSPECIFIED
;
134 case SWF::FILL_TILED_BITMAP_HARD
:
135 _type
= BitmapFill::TILED
;
136 _smoothingPolicy
= BitmapFill::SMOOTHING_OFF
;
139 case SWF::FILL_TILED_BITMAP
:
140 _type
= BitmapFill::TILED
;
143 case SWF::FILL_CLIPPED_BITMAP_HARD
:
144 _type
= BitmapFill::CLIPPED
;
145 _smoothingPolicy
= BitmapFill::SMOOTHING_OFF
;
148 case SWF::FILL_CLIPPED_BITMAP
:
149 _type
= BitmapFill::CLIPPED
;
158 BitmapFill::bitmap() const
161 return _bitmapInfo
->disposed() ? 0 : _bitmapInfo
.get();
164 _bitmapInfo
= _md
->getBitmap(_id
);
167 return _bitmapInfo
.get();
171 GradientFill::setLerp(const GradientFill
& a
, const GradientFill
& b
,
174 assert(type() == a
.type());
175 assert(_gradients
.size() == a
.recordCount());
176 assert(_gradients
.size() == b
.recordCount());
178 for (size_t i
= 0, e
= _gradients
.size(); i
< e
; ++i
) {
179 const GradientRecord
& ra
= a
.record(i
);
180 const GradientRecord
& rb
= b
.record(i
);
181 _gradients
[i
].ratio
= frnd(lerp
<float>(ra
.ratio
, rb
.ratio
, ratio
));
182 _gradients
[i
].color
.set_lerp(ra
.color
, rb
.color
, ratio
);
184 _matrix
.set_lerp(a
.matrix(), b
.matrix(), ratio
);
188 BitmapFill::setLerp(const BitmapFill
& a
, const BitmapFill
& b
, double ratio
)
190 _matrix
.set_lerp(a
.matrix(), b
.matrix(), ratio
);
194 readFills(SWFStream
& in
, SWF::TagType t
, movie_definition
& md
, bool readMorph
)
198 const SWF::FillType type
= static_cast<SWF::FillType
>(in
.read_u8());
201 log_parse(" FillStyle read type = 0x%X", +type
);
206 case SWF::FILL_SOLID
:
207 return readSolidFill(in
, t
, readMorph
);
209 case SWF::FILL_TILED_BITMAP_HARD
:
210 case SWF::FILL_CLIPPED_BITMAP_HARD
:
211 case SWF::FILL_TILED_BITMAP
:
212 case SWF::FILL_CLIPPED_BITMAP
:
213 return readBitmapFill(in
, type
, md
, readMorph
);
215 case SWF::FILL_LINEAR_GRADIENT
:
216 case SWF::FILL_RADIAL_GRADIENT
:
217 case SWF::FILL_FOCAL_GRADIENT
:
220 GradientFill::Type gr
;
222 case SWF::FILL_LINEAR_GRADIENT
:
223 gr
= GradientFill::LINEAR
;
225 case SWF::FILL_RADIAL_GRADIENT
:
226 case SWF::FILL_FOCAL_GRADIENT
:
227 gr
= GradientFill::RADIAL
;
233 SWFMatrix m
= readSWFMatrix(in
).invert();
234 GradientFill
gf(gr
, m
);
236 boost::optional
<FillStyle
> morph
;
238 SWFMatrix m2
= readSWFMatrix(in
).invert();
239 morph
= GradientFill(gr
, m2
);
243 const boost::uint8_t grad_props
= in
.read_u8();
245 const boost::uint8_t num_gradients
= grad_props
& 0xF;
247 log_parse(" gradients: num_gradients = %d", +num_gradients
);
250 if (!num_gradients
) {
251 IF_VERBOSE_MALFORMED_SWF(
252 log_swferror(_("No gradients!"));
254 throw ParserException();
257 GradientFill::GradientRecords recs
;
258 recs
.reserve(num_gradients
);
260 GradientFill::GradientRecords morphrecs
;
261 morphrecs
.reserve(num_gradients
);
263 for (size_t i
= 0; i
< num_gradients
; ++i
) {
264 recs
.push_back(readGradientRecord(in
, t
));
266 morphrecs
.push_back(readGradientRecord(in
, t
));
270 // A GradientFill may never have fewer than 2 colour stops. We've
271 // no tests to show what happens in that case for static fills.
272 // Dynamic fills are tested to display as a solid fill. In either
273 // case the renderer will bork if there is only 1 stop in a
275 if (num_gradients
== 1) {
276 const rgba c1
= recs
[0].color
;
278 const rgba c2
= morphrecs
[0].color
;
279 morph
= SolidFill(c2
);
281 return std::make_pair(SolidFill(c1
), morph
);
286 boost::get
<GradientFill
>(morph
->fill
).setRecords(morphrecs
);
290 if (t
== SWF::DEFINESHAPE4
|| t
== SWF::DEFINESHAPE4_
) {
292 const SWF::SpreadMode spread
=
293 static_cast<SWF::SpreadMode
>(grad_props
>> 6);
296 case SWF::GRADIENT_SPREAD_PAD
:
297 gf
.spreadMode
= GradientFill::PAD
;
299 case SWF::GRADIENT_SPREAD_REFLECT
:
300 gf
.spreadMode
= GradientFill::REFLECT
;
302 case SWF::GRADIENT_SPREAD_REPEAT
:
303 gf
.spreadMode
= GradientFill::REPEAT
;
306 IF_VERBOSE_MALFORMED_SWF(
307 log_swferror("Illegal spread mode in gradient "
312 // TODO: handle in GradientFill.
313 const SWF::InterpolationMode i
=
314 static_cast<SWF::InterpolationMode
>((grad_props
>> 4) & 3);
317 case SWF::GRADIENT_INTERPOLATION_NORMAL
:
318 case SWF::GRADIENT_INTERPOLATION_LINEAR
:
319 gf
.interpolation
= i
;
321 IF_VERBOSE_MALFORMED_SWF(
322 log_swferror("Illegal interpolation mode in "
323 "gradient definition.");
328 // A focal gradient also has a focal point.
329 if (type
== SWF::FILL_FOCAL_GRADIENT
) {
331 gf
.setFocalPoint(in
.read_short_sfixed());
335 boost::get
<GradientFill
>(morph
->fill
).
336 setFocalPoint(gf
.focalPoint());
339 return std::make_pair(gf
, morph
);
344 std::stringstream ss
;
345 ss
<< "Unknown fill style type " << +type
;
346 // This is a fatal error, we'll be leaving the stream
347 // read pointer in an unknown position.
348 throw ParserException(ss
.str());
353 // Sets this style to a blend of a and b. t = [0,1]
355 setLerp(FillStyle
& f
, const FillStyle
& a
, const FillStyle
& b
, double t
)
357 assert(t
>= 0 && t
<= 1);
359 boost::apply_visitor(SetLerp(a
.fill
, b
.fill
, t
), f
.fill
);
365 readSolidFill(SWFStream
& in
, SWF::TagType t
, bool readMorph
)
369 boost::optional
<FillStyle
> morph
;
372 if (t
== SWF::DEFINESHAPE3
|| t
== SWF::DEFINESHAPE4
||
373 t
== SWF::DEFINESHAPE4_
|| readMorph
) {
374 color
= readRGBA(in
);
377 othercolor
= readRGBA(in
);
378 morph
= SolidFill(othercolor
);
382 // For DefineMorphShape tags we should use morphFillStyle
383 assert(t
== SWF::DEFINESHAPE
|| t
== SWF::DEFINESHAPE2
);
388 log_parse(" color: %s", color
);
390 return std::make_pair(SolidFill(color
), morph
);
394 readBitmapFill(SWFStream
& in
, SWF::FillType type
, movie_definition
& md
,
399 const boost::uint16_t id
= in
.read_u16();
401 SWFMatrix m
= readSWFMatrix(in
).invert();
403 boost::optional
<FillStyle
> morph
;
405 SWFMatrix m2
= readSWFMatrix(in
).invert();
406 morph
= BitmapFill(type
, &md
, id
, m2
);
409 // For some reason, it looks like they store the inverse of the
410 // TWIPS-to-texcoords SWFMatrix.
411 return std::make_pair(BitmapFill(type
, &md
, id
, m
), morph
);
415 readGradientRecord(SWFStream
& in
, SWF::TagType tag
)
418 const boost::uint8_t ratio
= in
.read_u8();
421 case SWF::DEFINESHAPE
:
422 case SWF::DEFINESHAPE2
:
424 const rgba color
= readRGB(in
);
425 return GradientRecord(ratio
, color
);
430 const rgba color
= readRGBA(in
);
431 return GradientRecord(ratio
, color
);
434 } // anonymous namespace
437 operator<<(std::ostream
& os
, const BitmapFill::SmoothingPolicy
& p
)
440 case BitmapFill::SMOOTHING_UNSPECIFIED
:
443 case BitmapFill::SMOOTHING_ON
:
446 case BitmapFill::SMOOTHING_OFF
:
450 // cast to int required to avoid infinite recursion
451 os
<< "unknown " << +p
;