• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/draw_utils.h"
6 
7 #include <algorithm>
8 #include <math.h>
9 #include <vector>
10 
11 #include "base/logging.h"
12 #include "base/numerics/safe_math.h"
13 
14 namespace chrome_pdf {
15 
GetBlue(const uint32 & pixel)16 inline uint8 GetBlue(const uint32& pixel) {
17   return static_cast<uint8>(pixel & 0xFF);
18 }
19 
GetGreen(const uint32 & pixel)20 inline uint8 GetGreen(const uint32& pixel) {
21   return static_cast<uint8>((pixel >> 8) & 0xFF);
22 }
23 
GetRed(const uint32 & pixel)24 inline uint8 GetRed(const uint32& pixel) {
25   return static_cast<uint8>((pixel >> 16) & 0xFF);
26 }
27 
GetAlpha(const uint32 & pixel)28 inline uint8 GetAlpha(const uint32& pixel) {
29   return static_cast<uint8>((pixel >> 24) & 0xFF);
30 }
31 
MakePixel(uint8 red,uint8 green,uint8 blue,uint8 alpha)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);
37 }
38 
GradientChannel(uint8 start,uint8 end,double ratio)39 inline uint8 GradientChannel(uint8 start, uint8 end, double ratio) {
40   double new_channel = start - (static_cast<double>(start) - end) * ratio;
41   if (new_channel < 0)
42     return 0;
43   if (new_channel > 255)
44     return 255;
45   return static_cast<uint8>(new_channel + 0.5);
46 }
47 
ProcessColor(uint8 src_color,uint8 dest_color,uint8 alpha)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);
52 }
53 
AlphaBlend(const pp::ImageData & src,const pp::Rect & src_rc,pp::ImageData * dest,const pp::Point & dest_origin,uint8 alpha_adjustment)54 bool AlphaBlend(const pp::ImageData& src, const pp::Rect& src_rc,
55                 pp::ImageData* dest, const pp::Point& dest_origin,
56                 uint8 alpha_adjustment) {
57   const uint32_t* src_origin_pixel = src.GetAddr32(src_rc.point());
58   uint32_t* dest_origin_pixel = dest->GetAddr32(dest_origin);
59 
60   int height = src_rc.height();
61   int width = src_rc.width();
62   for (int y = 0; y < height; y++) {
63     const uint32_t* src_pixel = src_origin_pixel;
64     uint32_t* dest_pixel = dest_origin_pixel;
65     for (int x = 0; x < width; x++) {
66       uint8 alpha = static_cast<uint8>(static_cast<uint32_t>(alpha_adjustment) *
67           GetAlpha(*src_pixel) / 0xFF);
68       uint8 red = ProcessColor(GetRed(*src_pixel), GetRed(*dest_pixel), alpha);
69       uint8 green = ProcessColor(GetGreen(*src_pixel),
70                                  GetGreen(*dest_pixel), alpha);
71       uint8 blue = ProcessColor(GetBlue(*src_pixel),
72                                 GetBlue(*dest_pixel), alpha);
73       *dest_pixel = MakePixel(red, green, blue, GetAlpha(*dest_pixel));
74 
75       src_pixel++;
76       dest_pixel++;
77     }
78     src_origin_pixel = reinterpret_cast<const uint32_t*>(
79         reinterpret_cast<const char*>(src_origin_pixel) + src.stride());
80     dest_origin_pixel = reinterpret_cast<uint32_t*>(
81         reinterpret_cast<char*>(dest_origin_pixel) + dest->stride());
82   }
83   return true;
84 }
85 
GradientFill(pp::ImageData * image,const pp::Rect & rc,uint32 start_color,uint32 end_color,bool horizontal)86 void GradientFill(pp::ImageData* image, const pp::Rect& rc,
87                   uint32 start_color, uint32 end_color, bool horizontal) {
88   std::vector<uint32> colors;
89   colors.resize(horizontal ? rc.width() : rc.height());
90   for (size_t i = 0; i < colors.size(); ++i) {
91     double ratio = static_cast<double>(i) / colors.size();
92     colors[i] = MakePixel(
93         GradientChannel(GetRed(start_color), GetRed(end_color), ratio),
94         GradientChannel(GetGreen(start_color), GetGreen(end_color), ratio),
95         GradientChannel(GetBlue(start_color), GetBlue(end_color), ratio),
96         GradientChannel(GetAlpha(start_color), GetAlpha(end_color), ratio));
97   }
98 
99   if (horizontal) {
100     const void* data = &(colors[0]);
101     size_t size = colors.size() * 4;
102     uint32_t* origin_pixel = image->GetAddr32(rc.point());
103     for (int y = 0; y < rc.height(); y++) {
104       memcpy(origin_pixel, data, size);
105       origin_pixel = reinterpret_cast<uint32_t*>(
106           reinterpret_cast<char*>(origin_pixel) + image->stride());
107     }
108   } else {
109     uint32_t* origin_pixel = image->GetAddr32(rc.point());
110     for (int y = 0; y < rc.height(); y++) {
111       uint32_t* pixel = origin_pixel;
112       for (int x = 0; x < rc.width(); x++) {
113         *pixel = colors[y];
114         pixel++;
115       }
116       origin_pixel = reinterpret_cast<uint32_t*>(
117           reinterpret_cast<char*>(origin_pixel) + image->stride());
118     }
119   }
120 }
121 
GradientFill(pp::Instance * instance,pp::ImageData * image,const pp::Rect & dirty_rc,const pp::Rect & gradient_rc,uint32 start_color,uint32 end_color,bool horizontal,uint8 transparency)122 void GradientFill(pp::Instance* instance,
123                   pp::ImageData* image,
124                   const pp::Rect& dirty_rc,
125                   const pp::Rect& gradient_rc,
126                   uint32 start_color,
127                   uint32 end_color,
128                   bool horizontal,
129                   uint8 transparency) {
130   pp::Rect draw_rc = gradient_rc.Intersect(dirty_rc);
131   if (draw_rc.IsEmpty())
132     return;
133 
134   pp::ImageData gradient(instance, PP_IMAGEDATAFORMAT_BGRA_PREMUL,
135       gradient_rc.size(), false);
136 
137   GradientFill(&gradient, pp::Rect(pp::Point(), gradient_rc.size()),
138                start_color, end_color, horizontal);
139 
140   pp::Rect copy_rc(draw_rc);
141   copy_rc.Offset(-gradient_rc.x(), -gradient_rc.y());
142   AlphaBlend(gradient, copy_rc, image, draw_rc.point(), transparency);
143 }
144 
CopyImage(const pp::ImageData & src,const pp::Rect & src_rc,pp::ImageData * dest,const pp::Rect & dest_rc,bool stretch)145 void CopyImage(const pp::ImageData& src, const pp::Rect& src_rc,
146                pp::ImageData* dest, const pp::Rect& dest_rc,
147                bool stretch) {
148   DCHECK(src_rc.width() <= dest_rc.width() &&
149       src_rc.height() <= dest_rc.height());
150 
151   const uint32_t* src_origin_pixel = src.GetAddr32(src_rc.point());
152   uint32_t* dest_origin_pixel = dest->GetAddr32(dest_rc.point());
153   if (stretch) {
154     double x_ratio = static_cast<double>(src_rc.width()) / dest_rc.width();
155     double y_ratio = static_cast<double>(src_rc.height()) / dest_rc.height();
156     int32_t height = dest_rc.height();
157     int32_t width = dest_rc.width();
158     for (int32_t y = 0; y < height; ++y) {
159       uint32_t* dest_pixel = dest_origin_pixel;
160       for (int32_t x = 0; x < width; ++x) {
161         uint32 src_x = static_cast<uint32>(x * x_ratio);
162         uint32 src_y = static_cast<uint32>(y * y_ratio);
163         const uint32_t* src_pixel = src.GetAddr32(
164             pp::Point(src_rc.x() + src_x, src_rc.y() + src_y));
165         *dest_pixel = *src_pixel;
166         dest_pixel++;
167       }
168       dest_origin_pixel = reinterpret_cast<uint32_t*>(
169           reinterpret_cast<char*>(dest_origin_pixel) + dest->stride());
170     }
171   } else {
172     int32_t height = src_rc.height();
173     base::CheckedNumeric<int32_t> width_bytes = src_rc.width();
174     width_bytes *= 4;
175     for (int32_t y = 0; y < height; ++y) {
176       memcpy(dest_origin_pixel, src_origin_pixel, width_bytes.ValueOrDie());
177       src_origin_pixel = reinterpret_cast<const uint32_t*>(
178           reinterpret_cast<const char*>(src_origin_pixel) + src.stride());
179       dest_origin_pixel = reinterpret_cast<uint32_t*>(
180           reinterpret_cast<char*>(dest_origin_pixel) + dest->stride());
181     }
182   }
183 }
184 
FillRect(pp::ImageData * image,const pp::Rect & rc,uint32 color)185 void FillRect(pp::ImageData* image, const pp::Rect& rc, uint32 color) {
186   int height = rc.height();
187   if (height == 0)
188     return;
189 
190   // Fill in first row.
191   uint32_t* top_line = image->GetAddr32(rc.point());
192   int width = rc.width();
193   for (int x = 0; x < width; x++)
194     top_line[x] = color;
195 
196   // Fill in the rest of the rectangle.
197   int byte_width = width * 4;
198   uint32_t* cur_line = reinterpret_cast<uint32_t*>(
199           reinterpret_cast<char*>(top_line) + image->stride());
200   for (int y = 1; y < height; y++) {
201     memcpy(cur_line, top_line, byte_width);
202     cur_line = reinterpret_cast<uint32_t*>(
203             reinterpret_cast<char*>(cur_line) + image->stride());
204   }
205 }
206 
ShadowMatrix(uint32 depth,double factor,uint32 background)207 ShadowMatrix::ShadowMatrix(uint32 depth, double factor, uint32 background)
208     : depth_(depth), factor_(factor), background_(background) {
209   DCHECK(depth_ > 0);
210   matrix_.resize(depth_ * depth_);
211 
212   // pv - is a rounding power factor for smoothing corners.
213   // pv = 2.0 will make corners completely round.
214   const double pv = 4.0;
215   // pow_pv - cache to avoid recalculating pow(x, pv) every time.
216   std::vector<double> pow_pv(depth_, 0.0);
217 
218   double r = static_cast<double>(depth_);
219   double coef = 256.0 / pow(r, factor);
220 
221   for (uint32 y = 0; y < depth_; y++) {
222     // Since matrix is symmetrical, we can reduce the number of calculations
223     // by mirroring results.
224     for (uint32 x = 0; x <= y; x++) {
225       // Fill cache if needed.
226       if (pow_pv[x] == 0.0)
227         pow_pv[x] =  pow(x, pv);
228       if (pow_pv[y] == 0.0)
229         pow_pv[y] =  pow(y, pv);
230 
231       // v - is a value for the smoothing function.
232       // If x == 0 simplify calculations.
233       double v = (x == 0) ? y : pow(pow_pv[x] + pow_pv[y], 1 / pv);
234 
235       // Smoothing function.
236       // If factor == 1, smoothing will be linear from 0 to the end,
237       // if 0 < factor < 1, smoothing will drop faster near 0.
238       // if factor > 1, smoothing will drop faster near the end (depth).
239       double f = 256.0 - coef * pow(v, factor);
240 
241       uint8 alpha = 0;
242       if (f > kOpaqueAlpha)
243         alpha = kOpaqueAlpha;
244       else if (f < kTransparentAlpha)
245         alpha = kTransparentAlpha;
246       else
247         alpha = static_cast<uint8>(f);
248 
249       uint8 red = ProcessColor(0, GetRed(background), alpha);
250       uint8 green = ProcessColor(0, GetGreen(background), alpha);
251       uint8 blue = ProcessColor(0, GetBlue(background), alpha);
252       uint32 pixel = MakePixel(red, green, blue, GetAlpha(background));
253 
254       // Mirror matrix.
255       matrix_[y * depth_ + x] = pixel;
256       matrix_[x * depth_ + y] = pixel;
257     }
258   }
259 }
260 
~ShadowMatrix()261 ShadowMatrix::~ShadowMatrix() {
262 }
263 
PaintShadow(pp::ImageData * image,const pp::Rect & clip_rc,const pp::Rect & shadow_rc,const ShadowMatrix & matrix)264 void PaintShadow(pp::ImageData* image,
265                  const pp::Rect& clip_rc,
266                  const pp::Rect& shadow_rc,
267                  const ShadowMatrix& matrix) {
268   pp::Rect draw_rc = shadow_rc.Intersect(clip_rc);
269   if (draw_rc.IsEmpty())
270     return;
271 
272   int32 depth = static_cast<int32>(matrix.depth());
273   for (int32_t y = draw_rc.y(); y < draw_rc.bottom(); y++) {
274     for (int32_t x = draw_rc.x(); x < draw_rc.right(); x++) {
275       int32_t matrix_x = std::max(depth + shadow_rc.x() - x - 1,
276                                   depth - shadow_rc.right() + x);
277       int32_t matrix_y = std::max(depth + shadow_rc.y() - y - 1,
278                                   depth - shadow_rc.bottom() + y);
279       uint32_t* pixel = image->GetAddr32(pp::Point(x, y));
280 
281       if (matrix_x < 0)
282         matrix_x = 0;
283       else if (matrix_x >= static_cast<int32>(depth))
284         matrix_x = depth - 1;
285 
286       if (matrix_y < 0)
287         matrix_y = 0;
288       else if (matrix_y >= static_cast<int32>(depth))
289         matrix_y = depth - 1;
290 
291       *pixel = matrix.GetValue(matrix_x, matrix_y);
292     }
293   }
294 }
295 
DrawShadow(pp::ImageData * image,const pp::Rect & shadow_rc,const pp::Rect & object_rc,const pp::Rect & clip_rc,const ShadowMatrix & matrix)296 void DrawShadow(pp::ImageData* image,
297                 const pp::Rect& shadow_rc,
298                 const pp::Rect& object_rc,
299                 const pp::Rect& clip_rc,
300                 const ShadowMatrix& matrix) {
301   if (shadow_rc == object_rc)
302     return;  // Nothing to paint.
303 
304   // Fill top part.
305   pp::Rect rc(shadow_rc.point(),
306               pp::Size(shadow_rc.width(), object_rc.y() - shadow_rc.y()));
307   PaintShadow(image, rc.Intersect(clip_rc), shadow_rc, matrix);
308 
309   // Fill bottom part.
310   rc = pp::Rect(shadow_rc.x(), object_rc.bottom(),
311                 shadow_rc.width(), shadow_rc.bottom() - object_rc.bottom());
312   PaintShadow(image, rc.Intersect(clip_rc), shadow_rc, matrix);
313 
314   // Fill left part.
315   rc = pp::Rect(shadow_rc.x(), object_rc.y(),
316                 object_rc.x() - shadow_rc.x(), object_rc.height());
317   PaintShadow(image, rc.Intersect(clip_rc), shadow_rc, matrix);
318 
319   // Fill right part.
320   rc = pp::Rect(object_rc.right(), object_rc.y(),
321                 shadow_rc.right() - object_rc.right(), object_rc.height());
322   PaintShadow(image, rc.Intersect(clip_rc), shadow_rc, matrix);
323 }
324 
325 }  // namespace chrome_pdf
326 
327