1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
|
//
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
// Free Software Foundation, Inc
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#ifndef RENDER_HANDLER_H
#define RENDER_HANDLER_H
/// \page Renderer_intro Render handler introduction
///
/// Information for writing new render handlers.
///
/// The most important thing about drawing Flash shapes is to understand how
/// their fill styles work.
/// A single Flash character can contain any number shapes that use any number
/// of different fill styles and line styles. The shapes of a character are
/// defined by a number of "paths". Some important things about paths:
///
/// - A path is a list of connected straight lines and (quadratic bezier)
/// curves (=edges). Interesting to note is that in the Flash world there are
/// *no* primitive objects like circles, rectangles or similar. These objects
/// are always translated to lines and curves (a circle is a set of eight
/// curves).
///
/// - All paths together must by definition always build a fully closed shape.
/// You can't draw a rectangle with three edges, for example, contrary to
/// most graphics library polygon routines that connect the last anchor to
/// the first. However, a *single* path does *not* have to be closed. The
/// missing parts may be defined by other paths (you will see this makes
/// sense).
///
/// - Each path has up to two fill styles and no or one line style. The line
/// style should be obvious. The two fill styles define the fill to the left
/// (fill style zero) and to the right (fill style one) of the path if you
/// think of it like a vector. The fill style is defined by a index to a
/// list of previously defined fill style definitions. Index 0 means "no
/// style" and is equal to a fully transparent fill style ("hole", if you
/// wish).
///
/// - Paths are *never* self-intersecting.
///
/// Simple examples to understand this concept:
///
/// - A rectangle that contains another rectangle. Only the area between the
/// two rectangles is filled (so it looks like a "o"). In this case Flash
/// fill create two paths (one for each rectangle) and one fill style.
/// Assume both paths come in clockwise order, then the outer rectangle
/// will have fillstyle0=0 and fillstyle1=1. The inner rectangle will have
/// fillstyle0=1 and fillstyle1=0.
///
/// \code
/// +--------------------------------+
/// |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
/// |XXX+------------------------+XXX|
/// |XXX| |XXX|
/// |XXX+------------------------+XXX|
/// |XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|
/// +--------------------------------+
/// \endcode
///
/// - A rectangle is divided vertically in two halves, both having different
/// colors:
///
/// \code
/// +-------A-------+-------B--------+
/// |...............|################|
/// A...............C################B
/// |...............|################|
/// +-------A-------+-------B--------+
/// \endcode
///
/// Flash will probably produce three paths (A,B,C) and two fill styles.
/// Paths "A" and "B" will have just one fill style (fillstyle1 will be
/// zero) while path "C" (which contains only one straight line!!) will
/// have two fill styles. To be exact the horizontal edges would not even
/// be necessary to render this shape (for a scanline based renderer) but
/// they are necessary then the character is about to be rotated.
///
/// Now, these are simple examples but complex graphics can be compressed very
/// efficiently this way. Also, this method was most probably intended for a
/// renderer engine that can produce the final character in just one pass
/// (like the AGG backend does too).
/// \page region_update Detection of updated regions
///
/// (this applies to the whole Gnash playback architecture)
///
/// After advancing the root movie (see gnash::Gui::advance_movie) it is checked
/// which region of the stage has been changed visibly (by computing the
/// bounds around updated characters). This has two advantages:
///
/// 1st, it allows a renderer/gui combination to avoid re-rendering of
/// unchanged parts in the scene. When supported by the rendering engine
/// this can be a huge performance gain. The original Flash player does
/// that too, btw.
///
/// 2nd, it can detect still frames (like a stopped movie). gui.cpp can
/// detect these and completely avoid calling any rendering function.
///
/// Of course, the most critical part is detection of changes. There is a
/// method gnash::character::set_invalidated() which gets called whenever a
/// critical property of a instance gets updated, like when it changes
/// position, for example.
/// It's really important to *always* call set_invalidated() *before*
/// any call that changes the character instance in a visible way.
///
/// Even if no renderer really uses this information it has effects when
/// skipping unchanged frames. If necessary, this feature can be switched
/// off easily in gui.cpp (maybe using a runtime option?).
///
/// Note the updated region is only passed to the gnash::Gui, which is itself
/// responsible of informing the renderer
/// (see gnash::Gui::set_invalidated_region).
/// This is because it's pointless
/// to have a renderer which updates only a small part of the stage when
/// the GUI shows it all since the area around the region is undefined.
/// However, there can be a GUI which supports update regions without needing
/// the renderer to do so (for example, to save time during blitting).
/// The GUI can also completely ignore the region information.
///
/// It's also important to note that the bounds passed to the GUI are just
/// a hint and the GUI /is/ allowed to further process and alter the information
/// in any way.
///
/// As for the integer/float discussion: I used SWFRect (floats) because all
/// the bounds calculation involves floats anyway and so it's probably
/// faster than converting between ints and floats all the way.
#include <vector>
#include <boost/noncopyable.hpp>
#include "dsodefs.h" // for DSOEXPORT
#include "boost/shared_array.hpp"
#include "boost/scoped_ptr.hpp"
#include "GnashEnums.h"
#include "Range2d.h"
#include "Point2d.h"
#include "RGBA.h"
#include "log.h"
#include "snappingrange.h"
#include "SWFRect.h"
// Forward declarations.
namespace gnash {
class IOChannel;
class CachedBitmap;
class rgba;
class Transform;
class SWFMatrix;
class FillStyle;
class LineStyle;
class Shape;
class MorphShape;
// XXX: GnashImageProxy (delayed image rendering)
class GnashVaapiImageProxy;
namespace SWF {
class ShapeRecord;
}
namespace image {
class GnashImage;
}
}
namespace gnash {
/// Base class for render handlers.
//
/// You must define a subclass of Renderer, and pass an
/// instance to the core (RunResources) *before* any SWF parsing begins.
///
/// For more info see page \ref Renderer_intro.
class DSOEXPORT Renderer : boost::noncopyable
{
public:
Renderer(): _quality(QUALITY_HIGH) { }
virtual ~Renderer() {}
/// Return a description of this renderer.
virtual std::string description() const = 0;
/// ==================================================================
/// Interfaces for adjusting renderer output.
/// ==================================================================
/// Sets the x/y scale for the movie
virtual void set_scale(float /*xscale*/, float /*yscale*/) {}
/// Sets the x/y offset for the movie in pixels. This applies to all
/// graphics drawn except the background, which must be drawn for the
/// entire canvas, regardless of the translation.
virtual void set_translation(float /*xoff*/, float /*yoff*/) {}
void setQuality(Quality q) { _quality = q; }
/// ==================================================================
/// Caching utitilies for core.
/// ==================================================================
/// \brief
/// Given an image, returns a pointer to a CachedBitmap class
/// that can later be passed to FillStyleX_bitmap(), to set a
/// bitmap fill style.
virtual CachedBitmap *
createCachedBitmap(std::auto_ptr<image::GnashImage> im) = 0;
/// ==================================================================
/// Rendering Interface.
/// ==================================================================
/// Draws a video frame.
//
/// The frame has already been decoded and is available in RGB format only.
///
/// @param frame The RGB video buffer frame.
/// Ownership of the buffer is left to the caller.
///
/// @param mat The SWFMatrix with world coordinates used to retrieve the x
/// and y coordinate of the video object. The scaling of the SWFMatrix
/// only refers to the Flash instance, *not* to the video inside that
/// instance. When a video object is placed on the stage and the loaded
/// video is smaller, then the SWFMatrix is still an "identity
/// matrix". However, if the video object is scaled via ActionScript,
/// for example, then the SWFMatrix will change. This means the
/// renderer has to find the correct scaling for the video inside the
/// bounds.
///
/// @param bounds The minX/minY fields of this SWFRect are always zero.
/// The width and height determine the size of the Flash video instance
/// on the stage (in TWIPS) prior to SWFMatrix transformations.
///
virtual void drawVideoFrame(image::GnashImage* frame,
const Transform& xform, const SWFRect* bounds, bool smooth) = 0;
/// Draw a line-strip directly, using a thin, solid line.
//
/// Can be used to draw empty boxes and cursors.
///
/// @coords an array of 16-bit signed integer coordinates. Even indices
/// (and 0) are x coordinates, while uneven ones are y coordinates.
///
/// @vertex_count the number of x-y coordinates (vertices).
///
/// @color the color to be used to draw the line strip.
///
/// @mat the SWFMatrix to be used to transform the vertices.
virtual void drawLine(const std::vector<point>& coords,
const rgba& color, const SWFMatrix& mat) = 0;
/// Draw a simple, solid filled polygon with a thin (~1 pixel) outline.
//
/// This can't be used for
/// Flash shapes but is intended for internal drawings like bounding boxes
/// (editable text fields) and similar. The polygon should not contain
/// self-intersections. If you do not wish a outline or a fill, then simply
/// set the alpha value to zero.
///
/// The polygon need NOT be closed (ie: this function will automatically
/// add an additional vertex to close it.
///
/// When masked==false, then any potential mask currently active will be
/// ignored, otherwise it is respected.
///
virtual void draw_poly(const std::vector<point>& corners,
const rgba& fill, const rgba& outline, const SWFMatrix& mat,
bool masked) = 0;
virtual void drawShape(const SWF::ShapeRecord& shape,
const Transform& xform) = 0;
/// \brief
/// Draws a glyph (font character).
//
/// Glyphs are defined just like shape characters with the difference that
/// they do not have any fill or line styles.
/// Instead, the shape must be drawn using the given color (solid fill).
/// Please note that although the glyph paths may indicate subshapes,
/// the renderer is to ignore that information.
///
/// @param def
///
/// @param mat
///
/// @param color
virtual void drawGlyph(const SWF::ShapeRecord& rec, const rgba& color,
const SWFMatrix& mat) = 0;
/// Draw the current rendering buffer to an image file.
//
/// Although this can be done at any time during the rendering cycle
/// without harmful side effects, it's advisable only to do it when
/// between advance() calls, when the frame is fully renderered.
//
/// @param io The IOChannel to write to.
/// @param type The type of image output required (PNG, JPEG, GIF).
/// Note that not all FileTypes are images: rendering
/// to an FLV will not work.
virtual void renderToImage(boost::shared_ptr<IOChannel> /*io*/,
FileType /*type*/, int /*quality*/) const {
log_debug(_("Rendering to image not implemented for this "
"renderer"));
}
/// ==================================================================
/// Prepare drawing area and other utilities
/// ==================================================================
/// Sets the update region (called prior to begin_display).
//
/// The renderer
/// might do clipping and leave the region outside these bounds unchanged,
/// but he is allowed to change them if that makes sense. After rendering
/// a frame the area outside the invalidated region can be undefined and
/// is not used.
///
/// It is not required for all renderers.
/// Parameters are world coordinates (TWIPS).
///
/// For more info see page \ref region_update.
virtual void set_invalidated_regions(const InvalidatedRanges& /*ranges*/)
{
}
/// ==================================================================
/// Machinery for delayed images rendering (e.g. Xv with YV12 or VAAPI)
/// ==================================================================
///@{
typedef boost::shared_ptr<GnashVaapiImageProxy> RenderImage;
typedef std::vector<RenderImage> RenderImages;
// Get first render image
virtual RenderImages::const_iterator getFirstRenderImage() const
{ return _render_images.begin(); }
// Get last render image
virtual RenderImages::const_iterator getLastRenderImage() const
{ return _render_images.end(); }
///@}
///@{ Masks
///
/// Masks are defined by drawing calls enclosed by begin_submit_mask()
/// and end_submit_mask(). Between these two calls, no drawing is to
/// occur. The shapes rendered between the two calls define the
/// visible region of the mask. Graphics that are irrelevant in the
/// context of a mask (lines and fill styles, for example) should be
/// ignored. After use, disable_mask() is called to remove the mask.
///
/// Masks may be nested. That is, end_submit_mask() may be followed
/// by a call to begin_submit_mask(). The resulting mask shall be an
/// intersection of the previously created mask. disable_mask() shall
/// result in the disabling or destruction of the last created mask.
virtual void begin_submit_mask() = 0;
virtual void end_submit_mask() = 0;
virtual void disable_mask() = 0;
///@}
/// ==================================================================
/// Interface for querying the renderer.
/// ==================================================================
/// Converts world coordinates to pixel coordinates
virtual geometry::Range2d<int> world_to_pixel(const SWFRect& worldbounds)
const = 0;
geometry::Range2d<int> world_to_pixel(const geometry::Range2d<int>& wb)
const
{
if ((wb.isNull() || wb.isWorld())) return wb;
return world_to_pixel(SWFRect(wb.getMinX(), wb.getMinY(),
wb.getMaxX(), wb.getMaxY()));
}
/// Converts pixel coordinates to world coordinates (TWIPS)
virtual point pixel_to_world(int x, int y) const = 0;
geometry::Range2d<int> pixel_to_world(
const geometry::Range2d<int>& pixelbounds) const
{
point topleft = pixel_to_world(
pixelbounds.getMinX(), pixelbounds.getMinY());
point bottomright = pixel_to_world(
pixelbounds.getMaxX(), pixelbounds.getMaxY());
return geometry::Range2d<int> (topleft.x, topleft.y,
bottomright.x, bottomright.y);
}
/// \brief
/// Checks if the given bounds are (partially) in the current drawing
/// clipping area.
//
/// A render handler implementing invalidated bounds should implement
/// this method to avoid rendering of characters that are not visible
/// anyway.
/// By default this method always returns true, which will ensure correct
/// rendering. If possible, it should be re-implemented by the renderer
/// handler for better performance.
/// b contains TWIPS coordinates.
virtual bool bounds_in_clipping_area(const geometry::Range2d<int>& /*b*/)
const {
return true;
}
#ifdef USE_TESTSUITE
/// ==================================================================
/// Interfaces for testing only. Disabled when the testsuite isn't built.
/// ==================================================================
/// This function returns the color at any position in the stage. It is used
/// for automatic testing only, it should not be used for anything else!
/// x and y are pixel coordinates (<0 won't make any sense) and the color of
/// the nearest pixel is returned.
/// The function returns false when the coordinates are outside the
/// main frame buffer.
virtual bool getPixel(rgba& /*color_return*/, int /*x*/, int /*y*/) const {
log_debug("getPixel() not implemented for this renderer");
abort();
return false; // avoid compiler warning
}
void addRenderImage(boost::shared_ptr<GnashVaapiImageProxy> image) {
_render_images.push_back(image);
}
/// Returns the average RGB color for a square block on the stage. The
/// width and height of the block is defined by "radius" and x/y refer
/// to the center of the block. radius==1 equals getPixel() and radius==0
/// is illegal. For even "radius" values, the center point is not exactly
/// defined.
/// The function returns false when at least one pixel of the block was
/// outside the main frame buffer. In that case the value in color_return
/// is undefined.
/// This implementation is provided for simplicity. Renderers should
/// implement a specialized version for better performance.
virtual bool getAveragePixel(rgba& color_return, int x, int y,
unsigned int radius) const
{
assert(radius>0);
// optimization:
if (radius==1) return getPixel(color_return, x, y);
unsigned int r=0, g=0, b=0, a=0;
x -= radius/2;
y -= radius/2;
int xe = x+radius;
int ye = y+radius;
rgba pixel;
for (int yp=y; yp<ye; yp++)
for (int xp=x; xp<xe; xp++)
{
if (!getPixel(pixel, xp, yp))
return false;
r += pixel.m_r;
g += pixel.m_g;
b += pixel.m_b;
a += pixel.m_a;
}
int pcount = radius*radius;
color_return.m_r = r / pcount;
color_return.m_g = g / pcount;
color_return.m_b = b / pcount;
color_return.m_a = a / pcount;
return true;
}
/// \brief
/// Initializes the renderer for off-screen rendering used by the
/// testsuite.
///
/// This is a special function used for testcases ONLY. It is used by
/// MovieTester to prepare the renderer for off-screen rendering
/// without any GUI. The renderer is responsible to do all required
/// steps so that rendering is possible after the call. This may mean
/// that the renderer allocates memory for the given stage size.
///
/// The function returns false when the renderer is not able to do
/// off-screen rendering (default).
///
/// Note the function may be called again afterwards, resizing the stage.
/// Any number of calls to this function is possible and the renderer
/// is responsible to resize any buffer instead of wasting memory.
///
/// @param width stage width in pixels
///
/// @param height stage height in pixels
virtual bool initTestBuffer(unsigned /*width*/, unsigned /*height*/) {
return false;
}
/// Return color depth (bits per pixel) or 0 if unknown/unimplemented.
//
/// Default implementation returns 0 (unknown).
///
/// TODO: this should be a pure abstract function, just don't want
/// to scan ogl and cairo backend for an implementation *now*
/// but would be needed for automated testing...
virtual unsigned int getBitsPerPixel() const {
return 0;
}
#endif
class External
{
public:
/// Prepare the renderer for external rendering
//
/// Note that all arguments except the background colour are useless
/// outside the ogl renderer.
External(Renderer& r, const rgba& c, int w = 0, int h = 0,
float x0 = 0, float x1 = 0, float y0 = 0, float y1 = 0)
:
_r(r)
{
_r.begin_display(c, w, h, x0, x1, y0, y1);
}
~External() {
_r.end_display();
}
private:
Renderer& _r;
};
class Internal
{
public:
/// Prepare the renderer for internal rendering
Internal(Renderer& r, image::GnashImage& im)
:
_r(r),
_ext(_r.startInternalRender(im))
{
}
Renderer* renderer() const {
return _ext;
}
~Internal() {
_r.endInternalRender();
}
private:
Renderer& _r;
Renderer* _ext;
};
protected:
/// Kept in parallel with movie_root's setting.
Quality _quality;
// Delayed imaged to render
RenderImages _render_images;
private:
/// Bracket the displaying of a frame from a movie.
//
/// Set up to render a full frame from a movie and fills the
/// background. Sets up necessary transforms, to scale the
/// movie to fit within the given dimensions. Call
/// end_display() when you're done.
//
/// Most of the arguments are only for the ogl renderer. See documentation
/// in that class. Do not use these arguments for new renderers!
virtual void begin_display(const rgba& background_color,
int viewport_width, int viewport_height,
float x0, float x1, float y0, float y1) = 0;
virtual void end_display() = 0;
/// Setup the renderer to draw to an internal buffer.
//
/// Implementations are free to return a new renderer if they choose.
//
/// @return 0 if the renderer does not support this.
virtual Renderer* startInternalRender(image::GnashImage& buffer) = 0;
/// Finish internal rendering.
//
/// Any rendering after this function has been called must apply to the
/// external buffer.
virtual void endInternalRender() = 0;
};
} // namespace gnash
#endif
// Local Variables:
// mode: C++
// indent-tabs-mode: nil
// End:
|