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
ScaleInt(int val,float scale)31 int ScaleInt(int val, float scale) {
32 return static_cast<int>(val * scale);
33 }
34
ProgressControl()35 ProgressControl::ProgressControl()
36 : progress_(0.0),
37 device_scale_(1.0) {
38 }
39
~ProgressControl()40 ProgressControl::~ProgressControl() {
41 }
42
CreateProgressControl(uint32 id,bool visible,Control::Owner * delegate,double progress,float device_scale,const std::vector<pp::ImageData> & images,const pp::ImageData & background,const std::string & text)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
Reconfigure(const pp::ImageData & background,const std::vector<pp::ImageData> & images,float device_scale)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
CalculateLayout(pp::Instance * instance,const std::vector<pp::ImageData> & images,const pp::ImageData & background,const std::string & text,float device_scale,pp::Size * ctrl_size,pp::Rect * image_rc,pp::Rect * text_rc)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
GetImageIngex() const134 size_t ProgressControl::GetImageIngex() const {
135 return static_cast<size_t>((progress_ / 100.0) * images_.size());
136 }
137
Paint(pp::ImageData * image_data,const pp::Rect & rc)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
SetProgress(double progress)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
PrepareBackground()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
AdjustBackground()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