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.
5 #include "pdf/draw_utils.h"
11 #include "base/logging.h"
12 #include "base/numerics/safe_math.h"
14 namespace chrome_pdf
{
16 inline uint8
GetBlue(const uint32
& pixel
) {
17 return static_cast<uint8
>(pixel
& 0xFF);
20 inline uint8
GetGreen(const uint32
& pixel
) {
21 return static_cast<uint8
>((pixel
>> 8) & 0xFF);
24 inline uint8
GetRed(const uint32
& pixel
) {
25 return static_cast<uint8
>((pixel
>> 16) & 0xFF);
28 inline uint8
GetAlpha(const uint32
& pixel
) {
29 return static_cast<uint8
>((pixel
>> 24) & 0xFF);
32 inline uint32_t MakePixel(uint8 red
, uint8 green
, uint8 blue
, uint8 alpha
) {
33 return (static_cast<uint32_t>(alpha
) << 24) |
34 (static_cast<uint32_t>(red
) << 16) |
35 (static_cast<uint32_t>(green
) << 8) |
36 static_cast<uint32_t>(blue
);
39 inline uint8
GradientChannel(uint8 start
, uint8 end
, double ratio
) {
40 double new_channel
= start
- (static_cast<double>(start
) - end
) * ratio
;
43 if (new_channel
> 255)
45 return static_cast<uint8
>(new_channel
+ 0.5);
48 inline uint8
ProcessColor(uint8 src_color
, uint8 dest_color
, uint8 alpha
) {
49 uint32 processed
= static_cast<uint32
>(src_color
) * alpha
+
50 static_cast<uint32
>(dest_color
) * (0xFF - alpha
);
51 return static_cast<uint8
>((processed
/ 0xFF) & 0xFF);
54 inline bool ImageDataContainsRect(const pp::ImageData
& image_data
,
55 const pp::Rect
& rect
) {
56 return rect
.width() >= 0 && rect
.height() >= 0 &&
57 pp::Rect(image_data
.size()).Contains(rect
);
60 bool AlphaBlend(const pp::ImageData
& src
, const pp::Rect
& src_rc
,
61 pp::ImageData
* dest
, const pp::Point
& dest_origin
,
62 uint8 alpha_adjustment
) {
63 const uint32_t* src_origin_pixel
= src
.GetAddr32(src_rc
.point());
64 uint32_t* dest_origin_pixel
= dest
->GetAddr32(dest_origin
);
66 int height
= src_rc
.height();
67 int width
= src_rc
.width();
68 for (int y
= 0; y
< height
; y
++) {
69 const uint32_t* src_pixel
= src_origin_pixel
;
70 uint32_t* dest_pixel
= dest_origin_pixel
;
71 for (int x
= 0; x
< width
; x
++) {
72 uint8 alpha
= static_cast<uint8
>(static_cast<uint32_t>(alpha_adjustment
) *
73 GetAlpha(*src_pixel
) / 0xFF);
74 uint8 red
= ProcessColor(GetRed(*src_pixel
), GetRed(*dest_pixel
), alpha
);
75 uint8 green
= ProcessColor(GetGreen(*src_pixel
),
76 GetGreen(*dest_pixel
), alpha
);
77 uint8 blue
= ProcessColor(GetBlue(*src_pixel
),
78 GetBlue(*dest_pixel
), alpha
);
79 *dest_pixel
= MakePixel(red
, green
, blue
, GetAlpha(*dest_pixel
));
84 src_origin_pixel
= reinterpret_cast<const uint32_t*>(
85 reinterpret_cast<const char*>(src_origin_pixel
) + src
.stride());
86 dest_origin_pixel
= reinterpret_cast<uint32_t*>(
87 reinterpret_cast<char*>(dest_origin_pixel
) + dest
->stride());
92 void GradientFill(pp::ImageData
* image
, const pp::Rect
& rc
,
93 uint32 start_color
, uint32 end_color
, bool horizontal
) {
94 std::vector
<uint32
> colors
;
95 colors
.resize(horizontal
? rc
.width() : rc
.height());
96 for (size_t i
= 0; i
< colors
.size(); ++i
) {
97 double ratio
= static_cast<double>(i
) / colors
.size();
98 colors
[i
] = MakePixel(
99 GradientChannel(GetRed(start_color
), GetRed(end_color
), ratio
),
100 GradientChannel(GetGreen(start_color
), GetGreen(end_color
), ratio
),
101 GradientChannel(GetBlue(start_color
), GetBlue(end_color
), ratio
),
102 GradientChannel(GetAlpha(start_color
), GetAlpha(end_color
), ratio
));
106 const void* data
= &(colors
[0]);
107 size_t size
= colors
.size() * 4;
108 uint32_t* origin_pixel
= image
->GetAddr32(rc
.point());
109 for (int y
= 0; y
< rc
.height(); y
++) {
110 memcpy(origin_pixel
, data
, size
);
111 origin_pixel
= reinterpret_cast<uint32_t*>(
112 reinterpret_cast<char*>(origin_pixel
) + image
->stride());
115 uint32_t* origin_pixel
= image
->GetAddr32(rc
.point());
116 for (int y
= 0; y
< rc
.height(); y
++) {
117 uint32_t* pixel
= origin_pixel
;
118 for (int x
= 0; x
< rc
.width(); x
++) {
122 origin_pixel
= reinterpret_cast<uint32_t*>(
123 reinterpret_cast<char*>(origin_pixel
) + image
->stride());
128 void GradientFill(pp::Instance
* instance
,
129 pp::ImageData
* image
,
130 const pp::Rect
& dirty_rc
,
131 const pp::Rect
& gradient_rc
,
135 uint8 transparency
) {
136 pp::Rect draw_rc
= gradient_rc
.Intersect(dirty_rc
);
137 if (draw_rc
.IsEmpty())
140 pp::ImageData
gradient(instance
, PP_IMAGEDATAFORMAT_BGRA_PREMUL
,
141 gradient_rc
.size(), false);
143 GradientFill(&gradient
, pp::Rect(pp::Point(), gradient_rc
.size()),
144 start_color
, end_color
, horizontal
);
146 pp::Rect
copy_rc(draw_rc
);
147 copy_rc
.Offset(-gradient_rc
.x(), -gradient_rc
.y());
148 AlphaBlend(gradient
, copy_rc
, image
, draw_rc
.point(), transparency
);
151 void CopyImage(const pp::ImageData
& src
, const pp::Rect
& src_rc
,
152 pp::ImageData
* dest
, const pp::Rect
& dest_rc
,
154 if (src_rc
.IsEmpty() || !ImageDataContainsRect(src
, src_rc
))
157 pp::Rect
stretched_rc(dest_rc
.point(),
158 stretch
? dest_rc
.size() : src_rc
.size());
159 if (stretched_rc
.IsEmpty() || !ImageDataContainsRect(*dest
, stretched_rc
))
162 const uint32_t* src_origin_pixel
= src
.GetAddr32(src_rc
.point());
163 uint32_t* dest_origin_pixel
= dest
->GetAddr32(dest_rc
.point());
165 double x_ratio
= static_cast<double>(src_rc
.width()) / dest_rc
.width();
166 double y_ratio
= static_cast<double>(src_rc
.height()) / dest_rc
.height();
167 int32_t height
= dest_rc
.height();
168 int32_t width
= dest_rc
.width();
169 for (int32_t y
= 0; y
< height
; ++y
) {
170 uint32_t* dest_pixel
= dest_origin_pixel
;
171 for (int32_t x
= 0; x
< width
; ++x
) {
172 uint32 src_x
= static_cast<uint32
>(x
* x_ratio
);
173 uint32 src_y
= static_cast<uint32
>(y
* y_ratio
);
174 const uint32_t* src_pixel
= src
.GetAddr32(
175 pp::Point(src_rc
.x() + src_x
, src_rc
.y() + src_y
));
176 *dest_pixel
= *src_pixel
;
179 dest_origin_pixel
= reinterpret_cast<uint32_t*>(
180 reinterpret_cast<char*>(dest_origin_pixel
) + dest
->stride());
183 int32_t height
= src_rc
.height();
184 base::CheckedNumeric
<int32_t> width_bytes
= src_rc
.width();
186 for (int32_t y
= 0; y
< height
; ++y
) {
187 memcpy(dest_origin_pixel
, src_origin_pixel
, width_bytes
.ValueOrDie());
188 src_origin_pixel
= reinterpret_cast<const uint32_t*>(
189 reinterpret_cast<const char*>(src_origin_pixel
) + src
.stride());
190 dest_origin_pixel
= reinterpret_cast<uint32_t*>(
191 reinterpret_cast<char*>(dest_origin_pixel
) + dest
->stride());
196 void FillRect(pp::ImageData
* image
, const pp::Rect
& rc
, uint32 color
) {
197 int height
= rc
.height();
201 // Fill in first row.
202 uint32_t* top_line
= image
->GetAddr32(rc
.point());
203 int width
= rc
.width();
204 for (int x
= 0; x
< width
; x
++)
207 // Fill in the rest of the rectangle.
208 int byte_width
= width
* 4;
209 uint32_t* cur_line
= reinterpret_cast<uint32_t*>(
210 reinterpret_cast<char*>(top_line
) + image
->stride());
211 for (int y
= 1; y
< height
; y
++) {
212 memcpy(cur_line
, top_line
, byte_width
);
213 cur_line
= reinterpret_cast<uint32_t*>(
214 reinterpret_cast<char*>(cur_line
) + image
->stride());
218 ShadowMatrix::ShadowMatrix(uint32 depth
, double factor
, uint32 background
)
219 : depth_(depth
), factor_(factor
), background_(background
) {
221 matrix_
.resize(depth_
* depth_
);
223 // pv - is a rounding power factor for smoothing corners.
224 // pv = 2.0 will make corners completely round.
225 const double pv
= 4.0;
226 // pow_pv - cache to avoid recalculating pow(x, pv) every time.
227 std::vector
<double> pow_pv(depth_
, 0.0);
229 double r
= static_cast<double>(depth_
);
230 double coef
= 256.0 / pow(r
, factor
);
232 for (uint32 y
= 0; y
< depth_
; y
++) {
233 // Since matrix is symmetrical, we can reduce the number of calculations
234 // by mirroring results.
235 for (uint32 x
= 0; x
<= y
; x
++) {
236 // Fill cache if needed.
237 if (pow_pv
[x
] == 0.0)
238 pow_pv
[x
] = pow(x
, pv
);
239 if (pow_pv
[y
] == 0.0)
240 pow_pv
[y
] = pow(y
, pv
);
242 // v - is a value for the smoothing function.
243 // If x == 0 simplify calculations.
244 double v
= (x
== 0) ? y
: pow(pow_pv
[x
] + pow_pv
[y
], 1 / pv
);
246 // Smoothing function.
247 // If factor == 1, smoothing will be linear from 0 to the end,
248 // if 0 < factor < 1, smoothing will drop faster near 0.
249 // if factor > 1, smoothing will drop faster near the end (depth).
250 double f
= 256.0 - coef
* pow(v
, factor
);
253 if (f
> kOpaqueAlpha
)
254 alpha
= kOpaqueAlpha
;
255 else if (f
< kTransparentAlpha
)
256 alpha
= kTransparentAlpha
;
258 alpha
= static_cast<uint8
>(f
);
260 uint8 red
= ProcessColor(0, GetRed(background
), alpha
);
261 uint8 green
= ProcessColor(0, GetGreen(background
), alpha
);
262 uint8 blue
= ProcessColor(0, GetBlue(background
), alpha
);
263 uint32 pixel
= MakePixel(red
, green
, blue
, GetAlpha(background
));
266 matrix_
[y
* depth_
+ x
] = pixel
;
267 matrix_
[x
* depth_
+ y
] = pixel
;
272 ShadowMatrix::~ShadowMatrix() {
275 void PaintShadow(pp::ImageData
* image
,
276 const pp::Rect
& clip_rc
,
277 const pp::Rect
& shadow_rc
,
278 const ShadowMatrix
& matrix
) {
279 pp::Rect draw_rc
= shadow_rc
.Intersect(clip_rc
);
280 if (draw_rc
.IsEmpty())
283 int32 depth
= static_cast<int32
>(matrix
.depth());
284 for (int32_t y
= draw_rc
.y(); y
< draw_rc
.bottom(); y
++) {
285 for (int32_t x
= draw_rc
.x(); x
< draw_rc
.right(); x
++) {
286 int32_t matrix_x
= std::max(depth
+ shadow_rc
.x() - x
- 1,
287 depth
- shadow_rc
.right() + x
);
288 int32_t matrix_y
= std::max(depth
+ shadow_rc
.y() - y
- 1,
289 depth
- shadow_rc
.bottom() + y
);
290 uint32_t* pixel
= image
->GetAddr32(pp::Point(x
, y
));
294 else if (matrix_x
>= static_cast<int32
>(depth
))
295 matrix_x
= depth
- 1;
299 else if (matrix_y
>= static_cast<int32
>(depth
))
300 matrix_y
= depth
- 1;
302 *pixel
= matrix
.GetValue(matrix_x
, matrix_y
);
307 void DrawShadow(pp::ImageData
* image
,
308 const pp::Rect
& shadow_rc
,
309 const pp::Rect
& object_rc
,
310 const pp::Rect
& clip_rc
,
311 const ShadowMatrix
& matrix
) {
312 if (shadow_rc
== object_rc
)
313 return; // Nothing to paint.
316 pp::Rect
rc(shadow_rc
.point(),
317 pp::Size(shadow_rc
.width(), object_rc
.y() - shadow_rc
.y()));
318 PaintShadow(image
, rc
.Intersect(clip_rc
), shadow_rc
, matrix
);
321 rc
= pp::Rect(shadow_rc
.x(), object_rc
.bottom(),
322 shadow_rc
.width(), shadow_rc
.bottom() - object_rc
.bottom());
323 PaintShadow(image
, rc
.Intersect(clip_rc
), shadow_rc
, matrix
);
326 rc
= pp::Rect(shadow_rc
.x(), object_rc
.y(),
327 object_rc
.x() - shadow_rc
.x(), object_rc
.height());
328 PaintShadow(image
, rc
.Intersect(clip_rc
), shadow_rc
, matrix
);
331 rc
= pp::Rect(object_rc
.right(), object_rc
.y(),
332 shadow_rc
.right() - object_rc
.right(), object_rc
.height());
333 PaintShadow(image
, rc
.Intersect(clip_rc
), shadow_rc
, matrix
);
336 } // namespace chrome_pdf