[email protected] | 1b1e9eff | 2014-05-20 01:56:40 | [diff] [blame] | 1 | // Copyright (c) 2011 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 "pdf/progress_control.h" |
| 6 | |
| 7 | #include <algorithm> |
| 8 | |
| 9 | #include "base/logging.h" |
| 10 | #include "pdf/draw_utils.h" |
| 11 | #include "pdf/resource_consts.h" |
| 12 | #include "ppapi/cpp/dev/font_dev.h" |
| 13 | |
| 14 | namespace chrome_pdf { |
| 15 | |
| 16 | const double ProgressControl::kCompleted = 100.0; |
| 17 | |
| 18 | // There is a bug outputting text with alpha 0xFF (opaque) to an intermediate |
| 19 | // image. It outputs alpha channgel of the text pixels to 0xFF (transparent). |
| 20 | // And it breaks next alpha blending. |
| 21 | // For now, let's use alpha 0xFE to work around this bug. |
| 22 | // TODO(gene): investigate this bug. |
| 23 | const uint32 kProgressTextColor = 0xFEDDE6FC; |
| 24 | const uint32 kProgressTextSize = 16; |
| 25 | const uint32 kImageTextSpacing = 8; |
| 26 | const uint32 kTopPadding = 8; |
| 27 | const uint32 kBottomPadding = 12; |
| 28 | const uint32 kLeftPadding = 10; |
| 29 | const uint32 kRightPadding = 10; |
| 30 | |
| 31 | int ScaleInt(int val, float scale) { |
| 32 | return static_cast<int>(val * scale); |
| 33 | } |
| 34 | |
| 35 | ProgressControl::ProgressControl() |
| 36 | : progress_(0.0), |
| 37 | device_scale_(1.0) { |
| 38 | } |
| 39 | |
| 40 | ProgressControl::~ProgressControl() { |
| 41 | } |
| 42 | |
| 43 | bool ProgressControl::CreateProgressControl( |
| 44 | uint32 id, |
| 45 | bool visible, |
| 46 | Control::Owner* delegate, |
| 47 | double progress, |
| 48 | float device_scale, |
| 49 | const std::vector<pp::ImageData>& images, |
| 50 | const pp::ImageData& background, |
| 51 | const std::string& text) { |
| 52 | progress_ = progress; |
| 53 | text_ = text; |
| 54 | bool res = Control::Create(id, pp::Rect(), visible, delegate); |
| 55 | if (res) |
| 56 | Reconfigure(background, images, device_scale); |
| 57 | return res; |
| 58 | } |
| 59 | |
| 60 | void ProgressControl::Reconfigure(const pp::ImageData& background, |
| 61 | const std::vector<pp::ImageData>& images, |
| 62 | float device_scale) { |
| 63 | DCHECK(images.size() != 0); |
| 64 | images_ = images; |
| 65 | background_ = background; |
| 66 | device_scale_ = device_scale; |
| 67 | pp::Size ctrl_size; |
| 68 | CalculateLayout(owner()->GetInstance(), images_, background_, text_, |
| 69 | device_scale_, &ctrl_size, &image_rc_, &text_rc_); |
| 70 | pp::Rect rc(pp::Point(), ctrl_size); |
| 71 | Control::SetRect(rc, false); |
| 72 | PrepareBackground(); |
| 73 | } |
| 74 | |
| 75 | // static |
| 76 | void ProgressControl::CalculateLayout(pp::Instance* instance, |
| 77 | const std::vector<pp::ImageData>& images, |
| 78 | const pp::ImageData& background, |
| 79 | const std::string& text, |
| 80 | float device_scale, |
| 81 | pp::Size* ctrl_size, |
| 82 | pp::Rect* image_rc, |
| 83 | pp::Rect* text_rc) { |
| 84 | DCHECK(images.size() != 0); |
| 85 | int image_width = 0; |
| 86 | int image_height = 0; |
| 87 | for (size_t i = 0; i < images.size(); i++) { |
| 88 | image_width = std::max(image_width, images[i].size().width()); |
| 89 | image_height = std::max(image_height, images[i].size().height()); |
| 90 | } |
| 91 | |
| 92 | pp::FontDescription_Dev description; |
| 93 | description.set_family(PP_FONTFAMILY_SANSSERIF); |
| 94 | description.set_size(ScaleInt(kProgressTextSize, device_scale)); |
| 95 | description.set_weight(PP_FONTWEIGHT_BOLD); |
| 96 | pp::Font_Dev font(instance, description); |
| 97 | int text_length = font.MeasureSimpleText(text); |
| 98 | |
| 99 | pp::FontDescription_Dev desc; |
| 100 | PP_FontMetrics_Dev metrics; |
| 101 | font.Describe(&desc, &metrics); |
| 102 | int text_height = metrics.height; |
| 103 | |
| 104 | *ctrl_size = pp::Size( |
| 105 | image_width + text_length + |
| 106 | ScaleInt(kImageTextSpacing + kLeftPadding + kRightPadding, device_scale), |
| 107 | std::max(image_height, text_height) + |
| 108 | ScaleInt(kTopPadding + kBottomPadding, device_scale)); |
| 109 | |
| 110 | int offset_x = 0; |
| 111 | int offset_y = 0; |
| 112 | if (ctrl_size->width() < background.size().width()) { |
| 113 | offset_x += (background.size().width() - ctrl_size->width()) / 2; |
| 114 | ctrl_size->set_width(background.size().width()); |
| 115 | } |
| 116 | if (ctrl_size->height() < background.size().height()) { |
| 117 | offset_y += (background.size().height() - ctrl_size->height()) / 2; |
| 118 | ctrl_size->set_height(background.size().height()); |
| 119 | } |
| 120 | |
| 121 | *image_rc = pp::Rect(ScaleInt(kLeftPadding, device_scale) + offset_x, |
| 122 | ScaleInt(kTopPadding, device_scale) + offset_y, |
| 123 | image_width, |
| 124 | image_height); |
| 125 | |
| 126 | *text_rc = pp::Rect( |
| 127 | ctrl_size->width() - text_length - |
| 128 | ScaleInt(kRightPadding, device_scale) - offset_x, |
| 129 | (ctrl_size->height() - text_height) / 2, |
| 130 | text_length, |
| 131 | text_height); |
| 132 | } |
| 133 | |
| 134 | size_t ProgressControl::GetImageIngex() const { |
| 135 | return static_cast<size_t>((progress_ / 100.0) * images_.size()); |
| 136 | } |
| 137 | |
| 138 | void ProgressControl::Paint(pp::ImageData* image_data, const pp::Rect& rc) { |
| 139 | if (!visible()) |
| 140 | return; |
| 141 | |
| 142 | pp::Rect draw_rc = rect().Intersect(rc); |
| 143 | if (draw_rc.IsEmpty()) |
| 144 | return; |
| 145 | |
| 146 | pp::ImageData buffer(owner()->GetInstance(), ctrl_background_.format(), |
| 147 | ctrl_background_.size(), false); |
| 148 | CopyImage(ctrl_background_, pp::Rect(ctrl_background_.size()), |
| 149 | &buffer, pp::Rect(ctrl_background_.size()), false); |
| 150 | |
| 151 | size_t index = GetImageIngex(); |
| 152 | if (index >= images_.size()) |
| 153 | index = images_.size() - 1; |
| 154 | |
| 155 | AlphaBlend(images_[index], |
| 156 | pp::Rect(images_[index].size()), |
| 157 | &buffer, |
| 158 | image_rc_.point(), |
| 159 | kOpaqueAlpha); |
| 160 | |
| 161 | pp::Rect image_draw_rc(draw_rc); |
| 162 | image_draw_rc.Offset(-rect().x(), -rect().y()); |
| 163 | AlphaBlend(buffer, |
| 164 | image_draw_rc, |
| 165 | image_data, |
| 166 | draw_rc.point(), |
| 167 | transparency()); |
| 168 | } |
| 169 | |
| 170 | void ProgressControl::SetProgress(double progress) { |
| 171 | size_t old_index = GetImageIngex(); |
| 172 | progress_ = progress; |
| 173 | size_t new_index = GetImageIngex(); |
| 174 | if (progress_ >= kCompleted) { |
| 175 | progress_ = kCompleted; |
| 176 | owner()->OnEvent(id(), EVENT_ID_PROGRESS_COMPLETED, NULL); |
| 177 | } |
| 178 | if (visible() && old_index != new_index) |
| 179 | owner()->Invalidate(id(), rect()); |
| 180 | } |
| 181 | |
| 182 | void ProgressControl::PrepareBackground() { |
| 183 | AdjustBackground(); |
| 184 | |
| 185 | pp::FontDescription_Dev description; |
| 186 | description.set_family(PP_FONTFAMILY_SANSSERIF); |
| 187 | description.set_size(ScaleInt(kProgressTextSize, device_scale_)); |
| 188 | description.set_weight(PP_FONTWEIGHT_BOLD); |
| 189 | pp::Font_Dev font(owner()->GetInstance(), description); |
| 190 | |
| 191 | pp::FontDescription_Dev desc; |
| 192 | PP_FontMetrics_Dev metrics; |
| 193 | font.Describe(&desc, &metrics); |
| 194 | |
| 195 | pp::Point text_origin = pp::Point(text_rc_.x(), |
| 196 | (text_rc_.y() + text_rc_.bottom() + metrics.x_height) / 2); |
| 197 | font.DrawTextAt(&ctrl_background_, pp::TextRun_Dev(text_), text_origin, |
| 198 | kProgressTextColor, pp::Rect(ctrl_background_.size()), false); |
| 199 | } |
| 200 | |
| 201 | void ProgressControl::AdjustBackground() { |
| 202 | ctrl_background_ = pp::ImageData(owner()->GetInstance(), |
| 203 | PP_IMAGEDATAFORMAT_BGRA_PREMUL, |
| 204 | rect().size(), |
| 205 | false); |
| 206 | |
| 207 | if (rect().size() == background_.size()) { |
| 208 | CopyImage(background_, pp::Rect(background_.size()), |
| 209 | &ctrl_background_, pp::Rect(ctrl_background_.size()), false); |
| 210 | return; |
| 211 | } |
| 212 | |
| 213 | // We need to stretch background to new dimentions. To do so, we split |
| 214 | // background into 9 different parts. We copy corner rects (1,3,7,9) as is, |
| 215 | // stretch rectangles between corners (2,4,6,8) in 1 dimention, and |
| 216 | // stretch center rect (5) in 2 dimentions. |
| 217 | // |---|---|---| |
| 218 | // | 1 | 2 | 3 | |
| 219 | // |---|---|---| |
| 220 | // | 4 | 5 | 6 | |
| 221 | // |---|---|---| |
| 222 | // | 7 | 8 | 9 | |
| 223 | // |---|---|---| |
| 224 | int slice_x = background_.size().width() / 3; |
| 225 | int slice_y = background_.size().height() / 3; |
| 226 | |
| 227 | // Copy rect 1 |
| 228 | pp::Rect src_rc(0, 0, slice_x, slice_y); |
| 229 | pp::Rect dest_rc(0, 0, slice_x, slice_y); |
| 230 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, false); |
| 231 | |
| 232 | // Copy rect 3 |
| 233 | src_rc.set_x(background_.size().width() - slice_x); |
| 234 | dest_rc.set_x(ctrl_background_.size().width() - slice_x); |
| 235 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, false); |
| 236 | |
| 237 | // Copy rect 9 |
| 238 | src_rc.set_y(background_.size().height() - slice_y); |
| 239 | dest_rc.set_y(ctrl_background_.size().height() - slice_y); |
| 240 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, false); |
| 241 | |
| 242 | // Copy rect 7 |
| 243 | src_rc.set_x(0); |
| 244 | dest_rc.set_x(0); |
| 245 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, false); |
| 246 | |
| 247 | // Stretch rect 2 |
| 248 | src_rc = pp::Rect( |
| 249 | slice_x, 0, background_.size().width() - 2 * slice_x, slice_y); |
| 250 | dest_rc = pp::Rect( |
| 251 | slice_x, 0, ctrl_background_.size().width() - 2 * slice_x, slice_y); |
| 252 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, true); |
| 253 | |
| 254 | // Copy rect 8 |
| 255 | src_rc.set_y(background_.size().height() - slice_y); |
| 256 | dest_rc.set_y(ctrl_background_.size().height() - slice_y); |
| 257 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, true); |
| 258 | |
| 259 | // Stretch rect 4 |
| 260 | src_rc = pp::Rect( |
| 261 | 0, slice_y, slice_x, background_.size().height() - 2 * slice_y); |
| 262 | dest_rc = pp::Rect( |
| 263 | 0, slice_y, slice_x, ctrl_background_.size().height() - 2 * slice_y); |
| 264 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, true); |
| 265 | |
| 266 | // Copy rect 6 |
| 267 | src_rc.set_x(background_.size().width() - slice_x); |
| 268 | dest_rc.set_x(ctrl_background_.size().width() - slice_x); |
| 269 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, true); |
| 270 | |
| 271 | // Stretch rect 5 |
| 272 | src_rc = pp::Rect(slice_x, |
| 273 | slice_y, |
| 274 | background_.size().width() - 2 * slice_x, |
| 275 | background_.size().height() - 2 * slice_y); |
| 276 | dest_rc = pp::Rect(slice_x, |
| 277 | slice_y, |
| 278 | ctrl_background_.size().width() - 2 * slice_x, |
| 279 | ctrl_background_.size().height() - 2 * slice_y); |
| 280 | CopyImage(background_, src_rc, &ctrl_background_, dest_rc, true); |
| 281 | } |
| 282 | |
| 283 | } // namespace chrome_pdf |