// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef PPAPI_UTILITY_GRAPHICS_PAINT_MANAGER_H_ #define PPAPI_UTILITY_GRAPHICS_PAINT_MANAGER_H_ #include <vector> #include "ppapi/cpp/graphics_2d.h" #include "ppapi/utility/completion_callback_factory.h" #include "ppapi/utility/graphics/paint_aggregator.h" /// @file /// This file defines the API to convert the "plugin push" model of painting /// in PPAPI to a paint request at a later time. namespace pp { class Graphics2D; class Instance; class Point; class Rect; /// This class converts the "instance push" model of painting in PPAPI to a /// paint request at a later time. Usage is that you call Invalidate and /// Scroll, and implement the Client interface. Your OnPaint handler will /// then get called with coalesced paint events. /// /// This class is basically a <code>PaintAggregator</code> that groups updates, /// plus management of callbacks for scheduling paints. /// /// <strong>Example:</strong> /// /// <code> /// /// class MyClass : public pp::Instance, public PaintManager::Client { /// public: /// MyClass() { /// paint_manager_.Initialize(this, this, false); /// } /// /// void ViewChanged(const pp::Rect& position, const pp::Rect& clip) { /// paint_manager_.SetSize(position.size()); /// } /// /// void DoSomething() { /// // This function does something like respond to an event that causes /// // the screen to need updating. /// paint_manager_.InvalidateRect(some_rect); /// } /// /// // Implementation of PaintManager::Client /// virtual bool OnPaint(pp::Graphics2D& device, /// const pp::PaintUpdate& update) { /// // If our app needed scrolling, we would apply that first here. /// /// // Then we would either repaint the area returned by GetPaintBounds or /// // iterate through all the paint_rects. /// /// // The caller will call Flush() for us, so don't do that here. /// return true; /// } /// /// private: /// pp::PaintManager paint_manager_; /// }; /// </code> class PaintManager { public: class Client { public: /// OnPaint() paints the given invalid area of the instance to the given /// graphics device. Returns true if anything was painted. /// /// You are given the list of rects to paint in <code>paint_rects</code>, /// and the union of all of these rects in <code>paint_bounds</code>. You /// only have to paint the area inside each of the /// <code>paint_rects</code>, but can paint more if you want (some apps may /// just want to paint the union). /// /// Do not call Flush() on the graphics device, this will be done /// automatically if you return true from this function since the /// <code>PaintManager</code> needs to handle the callback. /// /// It is legal for you to cause invalidates inside of Paint which will /// then get executed as soon as the Flush for this update has completed. /// However, this is not very nice to the host system since it will spin the /// CPU, possibly updating much faster than necessary. It is best to have a /// 1/60 second timer to do an invalidate instead. This will limit your /// animation to the slower of 60Hz or "however fast Flush can complete." /// /// @param[in] graphics A <code>Graphics2D</code> to be painted. /// @param[in] paint_rects A list of rects to paint. /// @param[in] paint_bounds A union of the rects to paint. /// /// @return true if successful, otherwise false. virtual bool OnPaint(Graphics2D& graphics, const std::vector<Rect>& paint_rects, const Rect& paint_bounds) = 0; protected: // You shouldn't be doing deleting through this interface. virtual ~Client() {} }; /// Default constructor for creating an is_null() <code>PaintManager</code> /// object. If you use this version of the constructor, you must call /// Initialize() below. PaintManager(); /// A constructor to create a new <code>PaintManager</code> with an instance /// and client. /// /// <strong>Note:</strong> You will need to call SetSize() before this class /// will do anything. Normally you do this from the <code>ViewChanged</code> /// method of your instance. /// /// @param instance The instance using this paint manager to do its /// painting. Painting will automatically go to this instance and you don't /// have to manually bind any device context (this is all handled by the /// paint manager). /// /// @param client A non-owning pointer and must remain valid (normally the /// object implementing the Client interface will own the paint manager). /// /// @param is_always_opaque A flag passed to the device contexts that this /// class creates. Set this to true if your instance always draws an opaque /// image to the device. This is used as a hint to the browser that it does /// not need to do alpha blending, which speeds up painting. If you generate /// non-opqaue pixels or aren't sure, set this to false for more general /// blending. /// /// If you set is_always_opaque, your alpha channel should always be set to /// 0xFF or there may be painting artifacts. Being opaque will allow the /// browser to do a memcpy rather than a blend to paint the plugin, and this /// means your alpha values will get set on the page backing store. If these /// values are incorrect, it could mess up future blending. If you aren't /// sure, it is always correct to specify that it it not opaque. PaintManager(Instance* instance, Client* client, bool is_always_opaque); /// Destructor. ~PaintManager(); /// Initialize() must be called if you are using the 0-arg constructor. /// /// @param instance The instance using this paint manager to do its /// painting. Painting will automatically go to this instance and you don't /// have to manually bind any device context (this is all handled by the /// paint manager). /// @param client A non-owning pointer and must remain valid (normally the /// object implementing the Client interface will own the paint manager). /// @param is_always_opaque A flag passed to the device contexts that this /// class creates. Set this to true if your instance always draws an opaque /// image to the device. This is used as a hint to the browser that it does /// not need to do alpha blending, which speeds up painting. If you generate /// non-opqaue pixels or aren't sure, set this to false for more general /// blending. /// /// If you set <code>is_always_opaque</code>, your alpha channel should /// always be set to <code>0xFF</code> or there may be painting artifacts. /// Being opaque will allow the browser to do a memcpy rather than a blend /// to paint the plugin, and this means your alpha values will get set on the /// page backing store. If these values are incorrect, it could mess up /// future blending. If you aren't sure, it is always correct to specify that /// it it not opaque. void Initialize(Instance* instance, Client* client, bool is_always_opaque); /// Setter function setting the max ratio of paint rect area to scroll rect /// area that we will tolerate before downgrading the scroll into a repaint. /// /// If the combined area of paint rects contained within the scroll /// rect grows too large, then we might as well just treat /// the scroll rect as a paint rect. /// /// @param[in] area The max ratio of paint rect area to scroll rect area that /// we will tolerate before downgrading the scroll into a repaint. void set_max_redundant_paint_to_scroll_area(float area) { aggregator_.set_max_redundant_paint_to_scroll_area(area); } /// Setter function for setting the maximum number of paint rects. If we /// exceed this limit, then we'll start combining paint rects (refer to /// CombinePaintRects() for further information). This limiting can be /// important since there is typically some overhead in deciding what to /// paint. If your module is fast at doing these computations, raise this /// threshold, if your module is slow, lower it (probably requires some /// tuning to find the right value). /// /// @param[in] max_rects The maximum number of paint rects. void set_max_paint_rects(size_t max_rects) { aggregator_.set_max_paint_rects(max_rects); } /// SetSize() sets the size of the instance. If the size is the same as the /// previous call, this will be a NOP. If the size has changed, a new device /// will be allocated to the given size and a paint to that device will be /// scheduled. /// /// This function is intended to be called from <code>ViewChanged</code> with /// the size of the instance. Since it tracks the old size and only allocates /// when the size changes, you can always call this function without worrying /// about whether the size changed or ViewChanged() is called for another /// reason (like the position changed). /// /// @param new_size The new size for the instance. void SetSize(const Size& new_size); /// This function provides access to the underlying device in case you need /// it. If you have done a SetSize(), note that the graphics context won't be /// updated until right before the next call to OnPaint(). /// /// <strong>Note:</strong> If you call Flush on this device the paint manager /// will get very confused, don't do this! const Graphics2D& graphics() const { return graphics_; } /// This function provides access to the underlying device in case you need /// it. If you have done a SetSize(), note that the graphics context won't be /// updated until right before the next call to OnPaint(). /// /// <strong>Note:</strong> If you call Flush on this device the paint manager /// will get very confused, don't do this! Graphics2D& graphics() { return graphics_; } /// Invalidate() invalidate the entire instance. void Invalidate(); /// InvalidateRect() Invalidate the provided rect. /// /// @param[in] rect The <code>Rect</code> to be invalidated. void InvalidateRect(const Rect& rect); /// ScrollRect() scrolls the provided <code>clip_rect</code> by the /// <code>amount</code> argument. /// /// @param clip_rect The clip rectangle to scroll. /// @param amount The amount to scroll <code>clip_rect</code>. void ScrollRect(const Rect& clip_rect, const Point& amount); /// GetEffectiveSize() returns the size of the graphics context for the /// next paint operation. This is the pending size if a resize is pending /// (the instance has called SetSize() but we haven't actually painted it /// yet), or the current size of no resize is pending. /// /// @return The effective size. Size GetEffectiveSize() const; private: // Disallow copy and assign (these are unimplemented). PaintManager(const PaintManager&); PaintManager& operator=(const PaintManager&); // Makes sure there is a callback that will trigger a paint at a later time. // This will be either a Flush callback telling us we're allowed to generate // more data, or, if there's no flush callback pending, a manual call back // to the message loop via ExecuteOnMainThread. void EnsureCallbackPending(); // Does the client paint and executes a Flush if necessary. void DoPaint(); // Callback for asynchronous completion of Flush. void OnFlushComplete(int32_t result); // Callback for manual scheduling of paints when there is no flush callback // pending. void OnManualCallbackComplete(int32_t); Instance* instance_; // Non-owning pointer. See the constructor. Client* client_; bool is_always_opaque_; CompletionCallbackFactory<PaintManager> callback_factory_; // This graphics device will be is_null() if no graphics has been manually // set yet. Graphics2D graphics_; PaintAggregator aggregator_; // See comment for EnsureCallbackPending for more on how these work. bool manual_callback_pending_; bool flush_pending_; // When we get a resize, we don't bind right away (see SetSize). The // has_pending_resize_ tells us that we need to do a resize for the next // paint operation. When true, the new size is in pending_size_. bool has_pending_resize_; Size pending_size_; }; } // namespace pp #endif // PPAPI_UTILITY_GRAPHICS_PAINT_MANAGER_H_