1
// Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
3
// This program is free software; you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation; either version 3 of the License, or
6
// (at your option) any later version.
8
// This program is distributed in the hope that it will be useful,
9
// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
// GNU General Public License for more details.
13
// You should have received a copy of the GNU General Public License
14
// along with this program; if not, write to the Free Software
15
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
#include "ShapeRecord.h"
20
#include "SWFStream.h"
21
#include "movie_definition.h"
22
#include "fill_style.h"
24
#include "GnashNumeric.h"
25
#include "RunResources.h"
32
// Forward declarations
34
void readFillStyles(ShapeRecord::FillStyles& styles, SWFStream& in,
35
SWF::TagType tag, movie_definition& md, const RunResources& r);
36
void readLineStyles(ShapeRecord::LineStyles& styles, SWFStream& in,
37
SWF::TagType tag, movie_definition& md, const RunResources& r);
38
void computeBounds(rect& bounds, const ShapeRecord::Paths& paths,
39
const ShapeRecord::LineStyles& lineStyles, int swfVersion);
42
// Functors for path and style manipulation.
49
Lerp(typename T::const_iterator style1, typename T::const_iterator style2,
57
void operator()(typename T::value_type& st)
59
st.set_lerp(*_style1, *_style2, _ratio);
64
typename T::const_iterator _style1;
65
typename T::const_iterator _style2;
69
// Facilities for working with list of paths.
72
typedef SWF::ShapeRecord::Paths Paths;
75
PathList(const Paths& paths)
80
_nedges(computeNumberOfEdges(_paths))
83
/// Return number of edges in the path list
89
/// Get next edge in the path list.
91
/// After last edge in the list has been fetched,
92
/// next call to this function will return first
95
const Edge& getNextEdge()
97
const Edge& ret = _paths[_currpath][_curredge];
98
if ( ++_curredge >= _paths[_currpath].size() )
100
if ( ++_currpath >= _paths.size() )
102
// this is not really needed,
103
// but it's simpler to do so that
104
// to make next call fail or abort..
112
/// Compute total number of edges
113
static size_t computeNumberOfEdges(const Paths& paths)
116
for (Paths::const_iterator i = paths.begin(), e = paths.end();
136
} // anonymous namespace
139
ShapeRecord::ShapeRecord(SWFStream& in, SWF::TagType tag, movie_definition& m,
140
const RunResources& r)
146
ShapeRecord::setLerp(const ShapeRecord& a, const ShapeRecord& b,
150
// Update current bounds.
151
_bounds.set_lerp(a.getBounds(), b.getBounds(), ratio);
154
const FillStyles::const_iterator fs1 = a.fillStyles().begin();
155
const FillStyles::const_iterator fs2 = b.fillStyles().begin();
157
std::for_each(_fillStyles.begin(), _fillStyles.end(),
158
Lerp<FillStyles>(fs1, fs2, ratio));
161
const LineStyles::const_iterator ls1 = a.lineStyles().begin();
162
const LineStyles::const_iterator ls2 = b.lineStyles().begin();
164
std::for_each(_lineStyles.begin(), _lineStyles.end(),
165
Lerp<LineStyles>(ls1, ls2, ratio));
167
// This is used for cases in which number
168
// of paths in start shape and end shape are not
170
const Path empty_path;
171
const Edge empty_edge;
174
const Paths& paths1 = a.paths();
175
const Paths& paths2 = b.paths();
176
for (size_t i = 0, k = 0, n = 0; i < _paths.size(); i++)
179
const Path& p1 = i < paths1.size() ? paths1[i] : empty_path;
180
const Path& p2 = n < paths2.size() ? paths2[n] : empty_path;
182
const float new_ax = flerp(p1.ap.x, p2.ap.x, ratio);
183
const float new_ay = flerp(p1.ap.y, p2.ap.y, ratio);
185
p.reset(new_ax, new_ay, p1.getLeftFill(),
186
p2.getRightFill(), p1.getLineStyle());
189
const size_t len = p1.size();
190
p.m_edges.resize(len);
192
for (size_t j=0; j < p.size(); j++)
195
const Edge& e1 = j < p1.size() ? p1[j] : empty_edge;
197
const Edge& e2 = k < p2.size() ? p2[k] : empty_edge;
199
e.cp.x = static_cast<int>(flerp(e1.cp.x, e2.cp.x, ratio));
200
e.cp.y = static_cast<int>(flerp(e1.cp.y, e2.cp.y, ratio));
201
e.ap.x = static_cast<int>(flerp(e1.ap.x, e2.ap.x, ratio));
202
e.ap.y = static_cast<int>(flerp(e1.ap.y, e2.ap.y, ratio));
205
if (p2.size() <= k) {
215
ShapeRecord::read(SWFStream& in, SWF::TagType tag, movie_definition& m,
216
const RunResources& r)
219
/// TODO: is this correct?
220
const bool styleInfo = (tag == SWF::DEFINESHAPE ||
221
tag == SWF::DEFINESHAPE2 ||
222
tag == SWF::DEFINESHAPE3 ||
223
tag == SWF::DEFINESHAPE4 ||
224
tag == SWF::DEFINESHAPE4_);
231
std::string b = _bounds.toString();
232
log_parse(_(" bound rect: %s"), b);
235
// TODO: Store and use these. Unfinished.
236
if (tag == SWF::DEFINESHAPE4 || tag == SWF::DEFINESHAPE4_)
241
static_cast<void>(in.read_u8());
242
LOG_ONCE(log_unimpl("DEFINESHAPE4 edge boundaries and scales"));
245
readFillStyles(_fillStyles, in, tag, m, r);
246
readLineStyles(_lineStyles, in, tag, m, r);
249
if (tag == SWF::DEFINEFONT || tag == SWF::DEFINEFONT2 )
254
// Use read_u8 to force alignment.
256
boost::uint8_t num_bits = in.read_u8();
257
int num_fill_bits = (num_bits & 0xF0) >> 4;
258
int num_line_bits = (num_bits & 0x0F);
261
log_parse(_(" ShapeRecord(%s): fillbits %d, linebits %d"),
262
tag, num_fill_bits, num_line_bits);
265
if ( !num_fill_bits && !num_line_bits )
267
/// When reading font glyphs it happens to read 1 byte
268
/// past end boundary of a glyph due to fill/line bits being
271
/// Generally returning here seems to break morphs:
272
/// http://savannah.gnu.org/bugs/?21747
273
/// And other normal shapes:
274
/// http://savannah.gnu.org/bugs/?21923
275
/// http://savannah.gnu.org/bugs/?22000
277
/// So for now we only return if NOT reading a morph shape.
278
/// Pretty ugly... till next bug report.
281
if (tag == SWF::DEFINEFONT || tag == SWF::DEFINEFONT2 ||
282
tag == SWF::DEFINEFONT3)
284
log_debug("Skipping glyph read, being fill and line bits zero. "
285
"SWF tag is %d.", tag);
290
// These are state variables that keep the
291
// current position & style of the shape
292
// outline, and vary as we read the edge data.
294
// At the moment we just store each edge with
295
// the full necessary info to render it, which
296
// is simple but not optimally efficient.
308
bool isEdgeRecord = in.read_bit();
313
int flags = in.read_uint(5);
314
if (flags == SHAPE_END)
316
// Store the current path if any.
317
if (! current_path.empty())
319
_paths.push_back(current_path);
320
current_path.m_edges.resize(0);
324
if (flags & SHAPE_MOVE)
326
// Store the current path if any, and prepare a fresh one.
327
if (! current_path.empty())
329
_paths.push_back(current_path);
330
current_path.m_edges.resize(0);
333
int num_move_bits = in.read_uint(5);
334
in.ensureBits(2 * num_move_bits);
335
int move_x = in.read_sint(num_move_bits);
336
int move_y = in.read_sint(num_move_bits);
341
// Set the beginning of the path.
342
current_path.ap.x = x;
343
current_path.ap.y = y;
347
log_parse(_(" Shape read: moveto %d %d"), x, y);
351
if ((flags & SHAPE_FILLSTYLE0_CHANGE) && num_fill_bits > 0)
353
// fill_style_0_change = 1;
354
if (! current_path.empty())
356
_paths.push_back(current_path);
357
current_path.m_edges.resize(0);
358
current_path.ap.x = x;
359
current_path.ap.y = y;
361
in.ensureBits(num_fill_bits);
362
unsigned style = in.read_uint(num_fill_bits);
368
if (tag == SWF::DEFINEFONT || tag == SWF::DEFINEFONT2)
370
if ( style > 1 ) // 0:hide 1:renderer
372
IF_VERBOSE_MALFORMED_SWF(
373
log_swferror(_("Invalid fill style %d in "
374
"fillStyle0Change record for font tag "
375
"(0 or 1 valid). Set to 0."), style);
383
if ( style > _fillStyles.size() )
385
IF_VERBOSE_MALFORMED_SWF(
386
log_swferror(_("Invalid fill style %d in "
387
"fillStyle0Change record - %d defined. "
388
"Set to 0."), style, _fillStyles.size());
394
current_path.setLeftFill(style);
397
log_parse(_(" Shape read: fill0 (left) = %d"),
398
current_path.getLeftFill());
402
if ((flags & SHAPE_FILLSTYLE1_CHANGE) && num_fill_bits > 0)
404
// fill_style_1_change = 1;
405
if (! current_path.empty())
407
_paths.push_back(current_path);
408
current_path.m_edges.resize(0);
409
current_path.ap.x = x;
410
current_path.ap.y = y;
412
in.ensureBits(num_fill_bits);
413
unsigned style = in.read_uint(num_fill_bits);
419
if (tag == SWF::DEFINEFONT || tag == SWF::DEFINEFONT2)
421
if ( style > 1 ) // 0:hide 1:renderer
423
IF_VERBOSE_MALFORMED_SWF(
424
log_swferror(_("Invalid fill style %d in "
425
"fillStyle1Change record for font tag "
426
"(0 or 1 valid). Set to 0."), style);
434
if ( style > _fillStyles.size() )
436
IF_VERBOSE_MALFORMED_SWF(
437
log_swferror(_("Invalid fill style %d in "
438
"fillStyle1Change record - %d defined. "
439
"Set to 0."), style, _fillStyles.size());
444
current_path.setRightFill(style);
447
log_parse(_(" Shape read: fill1 (right) = %d"),
448
current_path.getRightFill());
452
if ((flags & SHAPE_LINESTYLE_CHANGE) && num_line_bits > 0)
454
// line_style_change = 1;
455
if (! current_path.empty())
457
_paths.push_back(current_path);
458
current_path.m_edges.resize(0);
459
current_path.ap.x = x;
460
current_path.ap.y = y;
462
in.ensureBits(num_line_bits);
463
unsigned style = in.read_uint(num_line_bits);
468
if (tag == SWF::DEFINEFONT || tag == SWF::DEFINEFONT2)
470
if ( style > 1 ) // 0:hide 1:renderer
472
IF_VERBOSE_MALFORMED_SWF(
473
log_swferror(_("Invalid line style %d in "
474
"lineStyleChange record for font tag "
475
"(0 or 1 valid). Set to 0."), style);
482
if (style > _lineStyles.size()) {
483
IF_VERBOSE_MALFORMED_SWF(
484
log_swferror(_("Invalid fill style %d in "
485
"lineStyleChange record - %d defined. "
486
"Set to 0."), style, _lineStyles.size());
491
current_path.setLineStyle(style);
494
log_parse(_("ShapeRecord: line %d"),
495
current_path.getLineStyle());
499
if (flags & SHAPE_HAS_NEW_STYLES)
503
IF_VERBOSE_MALFORMED_SWF(
504
log_swferror("Unexpected HasNewStyle flag in tag "
505
"%d shape record", tag);
510
log_parse(_("ShapeRecord: more fill styles"));
513
// Store the current path if any.
514
if (! current_path.empty())
516
_paths.push_back(current_path);
517
current_path.clear();
520
// Tack on an empty path signalling a new shape.
521
// @@ need better understanding of whether this is correct??!?!!
522
// @@ i.e., we should just start a whole new shape here, right?
523
_paths.push_back(Path());
524
_paths.back().m_new_shape = true;
526
fill_base = _fillStyles.size();
527
line_base = _lineStyles.size();
528
readFillStyles(_fillStyles, in, tag, m, r);
529
readLineStyles(_lineStyles, in, tag, m, r);
532
num_fill_bits = in.read_uint(4);
533
num_line_bits = in.read_uint(4);
540
bool edge_flag = in.read_bit();
544
int num_bits = 2 + in.read_uint(4);
546
in.ensureBits(4 * num_bits);
547
int cx = x + in.read_sint(num_bits);
548
int cy = y + in.read_sint(num_bits);
549
int ax = cx + in.read_sint(num_bits);
550
int ay = cy + in.read_sint(num_bits);
554
log_parse(_("ShapeRecord: curved edge "
555
"%d %d - %d %d - %d %d"), x, y, cx, cy, ax, ay);
558
current_path.m_edges.push_back(Edge(cx, cy, ax, ay));
566
int num_bits = 2 + in.read_uint(4);
567
bool line_flag = in.read_bit();
572
in.ensureBits(2 * num_bits);
573
dx = in.read_sint(num_bits);
574
dy = in.read_sint(num_bits);
579
bool vert_flag = in.read_bit();
583
in.ensureBits(num_bits);
584
dx = in.read_sint(num_bits);
589
in.ensureBits(num_bits);
590
dy = in.read_sint(num_bits);
596
log_parse(_("ShapeRecord: straight edge "
597
"%d %d - %d %d"), x, y, x + dx, y + dy);
600
current_path.m_edges.push_back(Edge(x + dx, y + dy,
610
// TODO: performance would be improved by computing
611
// the bounds as edges are parsed.
612
computeBounds(_bounds, _paths, _lineStyles, m.get_version());
614
#ifdef GNASH_DEBUG_SHAPE_BOUNDS
618
computeBounds(computedBounds, _paths, _lineStyles, m->get_version());
619
if ( computedBounds != m_bounds )
621
log_debug("Shape object read for tag %d contained embedded "
622
"bounds %s, while we computed bounds %s",
623
tag, m_bound, computedBounds);
631
// Read fill styles, and push them onto the given style array.
633
readFillStyles(ShapeRecord::FillStyles& styles, SWFStream& in,
634
SWF::TagType tag, movie_definition& m, const RunResources& r)
637
boost::uint16_t fill_style_count = in.read_u8();
638
if (tag != SWF::DEFINESHAPE)
640
if (fill_style_count == 0xFF)
643
fill_style_count = in.read_u16();
648
log_parse(_(" readFillStyles: count = %u"), fill_style_count);
652
styles.reserve(styles.size()+fill_style_count);
653
for (boost::uint16_t i = 0; i < fill_style_count; ++i) {
654
// TODO: add a fill_style constructor directly reading from stream
656
fs.read(in, tag, m, r);
657
styles.push_back(fs);
661
// Read line styles and push them onto the back of the given array.
663
readLineStyles(ShapeRecord::LineStyles& styles, SWFStream& in,
664
SWF::TagType tag, movie_definition& md, const RunResources& r)
667
int line_style_count = in.read_u8();
670
log_parse(_(" readLineStyles: count = %d"), line_style_count);
673
if (line_style_count == 0xFF)
676
line_style_count = in.read_u16();
678
log_parse(_(" readLineStyles: count2 = %d"), line_style_count);
683
for (int i = 0; i < line_style_count; i++)
685
styles.resize(styles.size() + 1);
686
styles.back().read(in, tag, md, r);
690
// Find the bounds of this shape, and store them in the given rectangle.
692
computeBounds(rect& bounds, const ShapeRecord::Paths& paths,
693
const ShapeRecord::LineStyles& lineStyles, int swfVersion)
697
for (unsigned int i = 0; i < paths.size(); i++)
699
const Path& p = paths[i];
701
unsigned thickness = 0;
704
// For glyph shapes m_line is allowed to be 1
705
// while no defined line styles are allowed.
706
if (lineStyles.empty()) {
707
// This is either a Glyph, for which m_line==1 is valid
708
// or a bug in the parser, which we have no way to
709
// check at this time
710
assert(p.m_line == 1);
714
thickness = lineStyles[p.m_line-1].getThickness();
717
p.expandBounds(bounds, thickness, swfVersion);
722
} // anonymous namespace