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 33namespace mojo { 34namespace examples { 35 36class PNGViewer; 37 38// TODO(aa): Hook up ZoomableMedia interface again. 39class 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 188class 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 211class 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 238MojoResult MojoMain(MojoHandle shell_handle) { 239 mojo::ApplicationRunnerChromium runner(new mojo::examples::PNGViewer); 240 return runner.Run(shell_handle); 241} 242