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 284