fix typo, add instructions about lowercase named strings
[gnash.git] / libcore / FillStyle.cpp
blob43c37963fdc4938b2f6889bceb42d579304bc23a
1 // FillStyle.cpp: Graphical region filling styles, for Gnash.
2 //
3 // Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
4 //
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.
9 //
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"
22 #include <iostream>
23 #include <boost/variant.hpp>
24 #include <boost/optional.hpp>
26 #include "smart_ptr.h"
27 #include "log.h"
28 #include "SWFStream.h"
29 #include "movie_definition.h"
30 #include "SWF.h"
31 #include "GnashException.h"
32 #include "GnashNumeric.h"
33 #include "Renderer.h"
34 #include "RunResources.h"
35 #include "GnashImage.h"
38 namespace gnash {
40 // Forward declarations
41 namespace {
42 OptionalFillPair readSolidFill(SWFStream& in, SWF::TagType t,
43 bool readMorph);
44 OptionalFillPair readBitmapFill(SWFStream& in, SWF::FillType type,
45 movie_definition& md, bool readMorph);
46 GradientRecord readGradientRecord(SWFStream& in, SWF::TagType tag);
49 namespace {
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<>
57 public:
58 SetLerp(const FillStyle::Fill& a, const FillStyle::Fill& b, double ratio)
60 _a(a),
61 _b(b),
62 _ratio(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);
72 private:
73 const FillStyle::Fill& _a;
74 const FillStyle::Fill& _b;
75 const double _ratio;
81 SWFMatrix
82 gradientMatrix(GradientFill::Type t, const SWFMatrix& m)
84 SWFMatrix base;
85 switch (t) {
86 case GradientFill::LINEAR:
87 base.set_translation(128, 0);
88 base.set_scale(1.0 / 128, 1.0 / 128);
89 break;
90 case GradientFill::RADIAL:
91 base.set_translation(32, 32);
92 base.set_scale(1.0 / 512, 1.0 / 512);
93 break;
95 base.concatenate(m);
96 return base;
99 GradientFill::GradientFill(Type t, const SWFMatrix& m,
100 const GradientRecords& recs)
102 spreadMode(PAD),
103 interpolation(SWF::GRADIENT_INTERPOLATION_NORMAL),
104 _focalPoint(0.0),
105 _gradients(recs),
106 _type(t),
107 _matrix(gradientMatrix(t, m))
109 assert(recs.empty() || recs.size() > 1);
112 void
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)
121 _type(),
122 _smoothingPolicy(),
123 _matrix(m),
124 _bitmapInfo(0),
125 _md(md),
126 _id(id)
128 assert(md);
130 _smoothingPolicy = md->get_version() >= 8 ?
131 BitmapFill::SMOOTHING_ON : BitmapFill::SMOOTHING_UNSPECIFIED;
133 switch (t) {
134 case SWF::FILL_TILED_BITMAP_HARD:
135 _type = BitmapFill::TILED;
136 _smoothingPolicy = BitmapFill::SMOOTHING_OFF;
137 break;
139 case SWF::FILL_TILED_BITMAP:
140 _type = BitmapFill::TILED;
141 break;
143 case SWF::FILL_CLIPPED_BITMAP_HARD:
144 _type = BitmapFill::CLIPPED;
145 _smoothingPolicy = BitmapFill::SMOOTHING_OFF;
146 break;
148 case SWF::FILL_CLIPPED_BITMAP:
149 _type = BitmapFill::CLIPPED;
150 break;
152 default:
153 std::abort();
157 const CachedBitmap*
158 BitmapFill::bitmap() const
160 if (_bitmapInfo) {
161 return _bitmapInfo->disposed() ? 0 : _bitmapInfo.get();
163 if (!_md) return 0;
164 _bitmapInfo = _md->getBitmap(_id);
166 // May still be 0!
167 return _bitmapInfo.get();
170 void
171 GradientFill::setLerp(const GradientFill& a, const GradientFill& b,
172 double ratio)
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);
187 void
188 BitmapFill::setLerp(const BitmapFill& a, const BitmapFill& b, double ratio)
190 _matrix.set_lerp(a.matrix(), b.matrix(), ratio);
193 OptionalFillPair
194 readFills(SWFStream& in, SWF::TagType t, movie_definition& md, bool readMorph)
197 in.ensureBytes(1);
198 const SWF::FillType type = static_cast<SWF::FillType>(in.read_u8());
200 IF_VERBOSE_PARSE(
201 log_parse(" FillStyle read type = 0x%X", +type);
204 switch (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;
221 switch (type) {
222 case SWF::FILL_LINEAR_GRADIENT:
223 gr = GradientFill::LINEAR;
224 break;
225 case SWF::FILL_RADIAL_GRADIENT:
226 case SWF::FILL_FOCAL_GRADIENT:
227 gr = GradientFill::RADIAL;
228 break;
229 default:
230 std::abort();
233 SWFMatrix m = readSWFMatrix(in).invert();
234 GradientFill gf(gr, m);
236 boost::optional<FillStyle> morph;
237 if (readMorph) {
238 SWFMatrix m2 = readSWFMatrix(in).invert();
239 morph = GradientFill(gr, m2);
242 in.ensureBytes(1);
243 const boost::uint8_t grad_props = in.read_u8();
245 const boost::uint8_t num_gradients = grad_props & 0xF;
246 IF_VERBOSE_PARSE(
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));
265 if (readMorph) {
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
274 // GradientFill.
275 if (num_gradients == 1) {
276 const rgba c1 = recs[0].color;
277 if (readMorph) {
278 const rgba c2 = morphrecs[0].color;
279 morph = SolidFill(c2);
281 return std::make_pair(SolidFill(c1), morph);
284 gf.setRecords(recs);
285 if (readMorph) {
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);
295 switch (spread) {
296 case SWF::GRADIENT_SPREAD_PAD:
297 gf.spreadMode = GradientFill::PAD;
298 break;
299 case SWF::GRADIENT_SPREAD_REFLECT:
300 gf.spreadMode = GradientFill::REFLECT;
301 break;
302 case SWF::GRADIENT_SPREAD_REPEAT:
303 gf.spreadMode = GradientFill::REPEAT;
304 break;
305 default:
306 IF_VERBOSE_MALFORMED_SWF(
307 log_swferror("Illegal spread mode in gradient "
308 "definition.");
312 // TODO: handle in GradientFill.
313 const SWF::InterpolationMode i =
314 static_cast<SWF::InterpolationMode>((grad_props >> 4) & 3);
316 switch (i) {
317 case SWF::GRADIENT_INTERPOLATION_NORMAL:
318 case SWF::GRADIENT_INTERPOLATION_LINEAR:
319 gf.interpolation = i;
320 default:
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) {
330 in.ensureBytes(2);
331 gf.setFocalPoint(in.read_short_sfixed());
334 if (readMorph) {
335 boost::get<GradientFill>(morph->fill).
336 setFocalPoint(gf.focalPoint());
339 return std::make_pair(gf, morph);
342 default:
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]
354 void
355 setLerp(FillStyle& f, const FillStyle& a, const FillStyle& b, double t)
357 assert(t >= 0 && t <= 1);
358 f.fill = a.fill;
359 boost::apply_visitor(SetLerp(a.fill, b.fill, t), f.fill);
362 namespace {
364 OptionalFillPair
365 readSolidFill(SWFStream& in, SWF::TagType t, bool readMorph)
367 rgba color;
369 boost::optional<FillStyle> morph;
371 // 0x00: solid fill
372 if (t == SWF::DEFINESHAPE3 || t == SWF::DEFINESHAPE4 ||
373 t == SWF::DEFINESHAPE4_ || readMorph) {
374 color = readRGBA(in);
375 if (readMorph) {
376 rgba othercolor;
377 othercolor = readRGBA(in);
378 morph = SolidFill(othercolor);
381 else {
382 // For DefineMorphShape tags we should use morphFillStyle
383 assert(t == SWF::DEFINESHAPE || t == SWF::DEFINESHAPE2);
384 color = readRGB(in);
387 IF_VERBOSE_PARSE(
388 log_parse(" color: %s", color);
390 return std::make_pair(SolidFill(color), morph);
393 OptionalFillPair
394 readBitmapFill(SWFStream& in, SWF::FillType type, movie_definition& md,
395 bool readMorph)
398 in.ensureBytes(2);
399 const boost::uint16_t id = in.read_u16();
401 SWFMatrix m = readSWFMatrix(in).invert();
403 boost::optional<FillStyle> morph;
404 if (readMorph) {
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);
414 GradientRecord
415 readGradientRecord(SWFStream& in, SWF::TagType tag)
417 in.ensureBytes(1);
418 const boost::uint8_t ratio = in.read_u8();
420 switch (tag) {
421 case SWF::DEFINESHAPE:
422 case SWF::DEFINESHAPE2:
424 const rgba color = readRGB(in);
425 return GradientRecord(ratio, color);
427 default:
428 break;
430 const rgba color = readRGBA(in);
431 return GradientRecord(ratio, color);
434 } // anonymous namespace
436 std::ostream&
437 operator<<(std::ostream& os, const BitmapFill::SmoothingPolicy& p)
439 switch (p) {
440 case BitmapFill::SMOOTHING_UNSPECIFIED:
441 os << "unspecified";
442 break;
443 case BitmapFill::SMOOTHING_ON:
444 os << "on";
445 break;
446 case BitmapFill::SMOOTHING_OFF:
447 os << "off";
448 break;
449 default:
450 // cast to int required to avoid infinite recursion
451 os << "unknown " << +p;
452 break;
454 return os;
457 } // namespace gnash
460 // Local Variables:
461 // mode: C++
462 // End: