Home | History | Annotate | Download | only in png_viewer
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <algorithm>
      6 
      7 #include "base/memory/scoped_ptr.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "base/strings/string_tokenizer.h"
     10 #include "mojo/application/application_runner_chromium.h"
     11 #include "mojo/examples/media_viewer/media_viewer.mojom.h"
     12 #include "mojo/public/c/system/main.h"
     13 #include "mojo/public/cpp/application/application_connection.h"
     14 #include "mojo/public/cpp/application/application_delegate.h"
     15 #include "mojo/public/cpp/application/application_impl.h"
     16 #include "mojo/public/cpp/application/interface_factory_impl.h"
     17 #include "mojo/public/cpp/application/service_provider_impl.h"
     18 #include "mojo/services/public/cpp/view_manager/types.h"
     19 #include "mojo/services/public/cpp/view_manager/view.h"
     20 #include "mojo/services/public/cpp/view_manager/view_manager.h"
     21 #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h"
     22 #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h"
     23 #include "mojo/services/public/cpp/view_manager/view_observer.h"
     24 #include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
     25 #include "skia/ext/platform_canvas.h"
     26 #include "skia/ext/refptr.h"
     27 #include "third_party/skia/include/core/SkBitmap.h"
     28 #include "third_party/skia/include/core/SkCanvas.h"
     29 #include "third_party/skia/include/core/SkPaint.h"
     30 #include "third_party/skia/include/core/SkScalar.h"
     31 #include "ui/gfx/codec/png_codec.h"
     32 
     33 namespace mojo {
     34 namespace examples {
     35 
     36 class PNGViewer;
     37 
     38 // TODO(aa): Hook up ZoomableMedia interface again.
     39 class PNGView : public ViewManagerDelegate, public ViewObserver {
     40  public:
     41   static void Spawn(URLResponsePtr response,
     42                     ServiceProviderImpl* exported_services,
     43                     scoped_ptr<ServiceProvider> imported_services,
     44                     Shell* shell) {
     45     // PNGView deletes itself when its View is destroyed.
     46     new PNGView(
     47         response.Pass(), exported_services, imported_services.Pass(), shell);
     48   }
     49 
     50  private:
     51   static const uint16_t kMaxZoomPercentage = 400;
     52   static const uint16_t kMinZoomPercentage = 20;
     53   static const uint16_t kDefaultZoomPercentage = 100;
     54   static const uint16_t kZoomStep = 20;
     55 
     56   PNGView(URLResponsePtr response,
     57           ServiceProviderImpl* exported_services,
     58           scoped_ptr<ServiceProvider> imported_services,
     59           Shell* shell)
     60       : imported_services_(imported_services.Pass()),
     61         root_(NULL),
     62         view_manager_client_factory_(shell, this),
     63         zoom_percentage_(kDefaultZoomPercentage) {
     64     exported_services->AddService(&view_manager_client_factory_);
     65     DecodePNG(response.Pass());
     66   }
     67 
     68   virtual ~PNGView() {
     69     if (root_)
     70       root_->RemoveObserver(this);
     71   }
     72 
     73   // Overridden from ViewManagerDelegate:
     74   virtual void OnEmbed(ViewManager* view_manager,
     75                        View* root,
     76                        ServiceProviderImpl* exported_services,
     77                        scoped_ptr<ServiceProvider> imported_services) OVERRIDE {
     78     root_ = root;
     79     root_->AddObserver(this);
     80     root_->SetColor(SK_ColorGRAY);
     81     if (!bitmap_.isNull())
     82       DrawBitmap();
     83   }
     84 
     85   virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE {
     86     // TODO(aa): Need to figure out how shutdown works.
     87   }
     88 
     89   // Overridden from ViewObserver:
     90   virtual void OnViewBoundsChanged(View* view,
     91                                    const gfx::Rect& old_bounds,
     92                                    const gfx::Rect& new_bounds) OVERRIDE {
     93     DCHECK_EQ(view, root_);
     94     DrawBitmap();
     95   }
     96 
     97   virtual void OnViewDestroyed(View* view) OVERRIDE {
     98     DCHECK_EQ(view, root_);
     99     delete this;
    100   }
    101 
    102   void DecodePNG(URLResponsePtr response) {
    103     int content_length = GetContentLength(response->headers);
    104     scoped_ptr<unsigned char[]> data(new unsigned char[content_length]);
    105     unsigned char* buf = data.get();
    106     uint32_t bytes_remaining = content_length;
    107     uint32_t num_bytes = bytes_remaining;
    108     while (bytes_remaining > 0) {
    109       MojoResult result = ReadDataRaw(
    110           response->body.get(), buf, &num_bytes, MOJO_READ_DATA_FLAG_NONE);
    111       if (result == MOJO_RESULT_SHOULD_WAIT) {
    112         Wait(response->body.get(),
    113              MOJO_HANDLE_SIGNAL_READABLE,
    114              MOJO_DEADLINE_INDEFINITE);
    115       } else if (result == MOJO_RESULT_OK) {
    116         buf += num_bytes;
    117         num_bytes = bytes_remaining -= num_bytes;
    118       } else {
    119         break;
    120       }
    121     }
    122 
    123     gfx::PNGCodec::Decode(static_cast<const unsigned char*>(data.get()),
    124                           content_length,
    125                           &bitmap_);
    126   }
    127 
    128   void DrawBitmap() {
    129     if (!root_)
    130       return;
    131 
    132     skia::RefPtr<SkCanvas> canvas(skia::AdoptRef(skia::CreatePlatformCanvas(
    133         root_->bounds().width(), root_->bounds().height(), true)));
    134     canvas->drawColor(SK_ColorGRAY);
    135     SkPaint paint;
    136     SkScalar scale =
    137         SkFloatToScalar(zoom_percentage_ * 1.0f / kDefaultZoomPercentage);
    138     canvas->scale(scale, scale);
    139     canvas->drawBitmap(bitmap_, 0, 0, &paint);
    140     root_->SetContents(skia::GetTopDevice(*canvas)->accessBitmap(true));
    141   }
    142 
    143   void ZoomIn() {
    144     if (zoom_percentage_ >= kMaxZoomPercentage)
    145       return;
    146     zoom_percentage_ += kZoomStep;
    147     DrawBitmap();
    148   }
    149 
    150   void ZoomOut() {
    151     if (zoom_percentage_ <= kMinZoomPercentage)
    152       return;
    153     zoom_percentage_ -= kZoomStep;
    154     DrawBitmap();
    155   }
    156 
    157   void ZoomToActualSize() {
    158     if (zoom_percentage_ == kDefaultZoomPercentage)
    159       return;
    160     zoom_percentage_ = kDefaultZoomPercentage;
    161     DrawBitmap();
    162   }
    163 
    164   int GetContentLength(const Array<String>& headers) {
    165     for (size_t i = 0; i < headers.size(); ++i) {
    166       base::StringTokenizer t(headers[i], ": ;=");
    167       while (t.GetNext()) {
    168         if (!t.token_is_delim() && t.token() == "Content-Length") {
    169           while (t.GetNext()) {
    170             if (!t.token_is_delim())
    171               return atoi(t.token().c_str());
    172           }
    173         }
    174       }
    175     }
    176     return 0;
    177   }
    178 
    179   SkBitmap bitmap_;
    180   scoped_ptr<ServiceProvider> imported_services_;
    181   View* root_;
    182   ViewManagerClientFactory view_manager_client_factory_;
    183   uint16_t zoom_percentage_;
    184 
    185   DISALLOW_COPY_AND_ASSIGN(PNGView);
    186 };
    187 
    188 class ContentHandlerImpl : public InterfaceImpl<ContentHandler> {
    189  public:
    190   explicit ContentHandlerImpl(Shell* shell) : shell_(shell) {}
    191   virtual ~ContentHandlerImpl() {}
    192 
    193  private:
    194   // Overridden from ContentHandler:
    195   virtual void OnConnect(
    196       const mojo::String& url,
    197       URLResponsePtr response,
    198       InterfaceRequest<ServiceProvider> service_provider) OVERRIDE {
    199     ServiceProviderImpl* exported_services = new ServiceProviderImpl();
    200     BindToRequest(exported_services, &service_provider);
    201     scoped_ptr<ServiceProvider> remote(
    202         exported_services->CreateRemoteServiceProvider());
    203     PNGView::Spawn(response.Pass(), exported_services, remote.Pass(), shell_);
    204   }
    205 
    206   Shell* shell_;
    207 
    208   DISALLOW_COPY_AND_ASSIGN(ContentHandlerImpl);
    209 };
    210 
    211 class PNGViewer : public ApplicationDelegate {
    212  public:
    213   PNGViewer() {}
    214  private:
    215   // Overridden from ApplicationDelegate:
    216   virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE {
    217     content_handler_factory_.reset(
    218         new InterfaceFactoryImplWithContext<ContentHandlerImpl, Shell>(
    219             app->shell()));
    220   }
    221 
    222   // Overridden from ApplicationDelegate:
    223   virtual bool ConfigureIncomingConnection(ApplicationConnection* connection)
    224       MOJO_OVERRIDE {
    225     connection->AddService(content_handler_factory_.get());
    226     return true;
    227   }
    228 
    229   scoped_ptr<InterfaceFactoryImplWithContext<ContentHandlerImpl, Shell> >
    230       content_handler_factory_;
    231 
    232   DISALLOW_COPY_AND_ASSIGN(PNGViewer);
    233 };
    234 
    235 }  // namespace examples
    236 }  // namespace mojo
    237 
    238 MojoResult MojoMain(MojoHandle shell_handle) {
    239   mojo::ApplicationRunnerChromium runner(new mojo::examples::PNGViewer);
    240   return runner.Run(shell_handle);
    241 }
    242