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