1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
// Copyright 2011 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "pdf/draw_utils/shadow.h"
#include <math.h>
#include <stddef.h>
#include <algorithm>
#include "base/check_op.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
namespace chrome_pdf {
namespace draw_utils {
constexpr uint8_t kOpaqueAlpha = 0xFF;
constexpr uint8_t kTransparentAlpha = 0x00;
inline uint8_t GetBlue(const uint32_t& pixel) {
return static_cast<uint8_t>(pixel & 0xFF);
}
inline uint8_t GetGreen(const uint32_t& pixel) {
return static_cast<uint8_t>((pixel >> 8) & 0xFF);
}
inline uint8_t GetRed(const uint32_t& pixel) {
return static_cast<uint8_t>((pixel >> 16) & 0xFF);
}
inline uint8_t GetAlpha(const uint32_t& pixel) {
return static_cast<uint8_t>((pixel >> 24) & 0xFF);
}
inline uint32_t MakePixel(uint8_t red,
uint8_t green,
uint8_t blue,
uint8_t alpha) {
return (static_cast<uint32_t>(alpha) << 24) |
(static_cast<uint32_t>(red) << 16) |
(static_cast<uint32_t>(green) << 8) | static_cast<uint32_t>(blue);
}
inline uint8_t ProcessColor(uint8_t src_color,
uint8_t dest_color,
uint8_t alpha) {
uint32_t processed = static_cast<uint32_t>(src_color) * alpha +
static_cast<uint32_t>(dest_color) * (0xFF - alpha);
return static_cast<uint8_t>((processed / 0xFF) & 0xFF);
}
ShadowMatrix::ShadowMatrix(uint32_t depth, double factor, uint32_t background)
: depth_(depth) {
DCHECK_GT(depth_, 0U);
matrix_.resize(depth_ * depth_);
// pv - is a rounding power factor for smoothing corners.
// pv = 2.0 will make corners completely round.
constexpr double pv = 4.0;
// pow_pv - cache to avoid recalculating pow(x, pv) every time.
std::vector<double> pow_pv(depth_, 0.0);
double r = static_cast<double>(depth_);
double coef = 256.0 / pow(r, factor);
for (uint32_t y = 0; y < depth_; y++) {
// Since matrix is symmetrical, we can reduce the number of calculations
// by mirroring results.
for (uint32_t x = 0; x <= y; x++) {
// Fill cache if needed.
if (pow_pv[x] == 0.0)
pow_pv[x] = pow(x, pv);
if (pow_pv[y] == 0.0)
pow_pv[y] = pow(y, pv);
// v - is a value for the smoothing function.
// If x == 0 simplify calculations.
double v = (x == 0) ? y : pow(pow_pv[x] + pow_pv[y], 1 / pv);
// Smoothing function.
// If factor == 1, smoothing will be linear from 0 to the end,
// if 0 < factor < 1, smoothing will drop faster near 0.
// if factor > 1, smoothing will drop faster near the end (depth).
double f = 256.0 - coef * pow(v, factor);
uint8_t alpha = 0;
if (f > kOpaqueAlpha)
alpha = kOpaqueAlpha;
else if (f < kTransparentAlpha)
alpha = kTransparentAlpha;
else
alpha = static_cast<uint8_t>(f);
uint8_t red = ProcessColor(0, GetRed(background), alpha);
uint8_t green = ProcessColor(0, GetGreen(background), alpha);
uint8_t blue = ProcessColor(0, GetBlue(background), alpha);
uint32_t pixel = MakePixel(red, green, blue, GetAlpha(background));
// Mirror matrix.
matrix_[y * depth_ + x] = pixel;
matrix_[x * depth_ + y] = pixel;
}
}
}
ShadowMatrix::~ShadowMatrix() = default;
namespace {
void PaintShadow(SkBitmap& image,
const gfx::Rect& clip_rc,
const gfx::Rect& shadow_rc,
const ShadowMatrix& matrix) {
gfx::Rect draw_rc = gfx::IntersectRects(shadow_rc, clip_rc);
if (draw_rc.IsEmpty())
return;
int32_t depth = static_cast<int32_t>(matrix.depth());
for (int32_t y = draw_rc.y(); y < draw_rc.bottom(); y++) {
for (int32_t x = draw_rc.x(); x < draw_rc.right(); x++) {
int32_t matrix_x = std::max(depth + shadow_rc.x() - x - 1,
depth - shadow_rc.right() + x);
int32_t matrix_y = std::max(depth + shadow_rc.y() - y - 1,
depth - shadow_rc.bottom() + y);
uint32_t* pixel = image.getAddr32(x, y);
if (matrix_x < 0)
matrix_x = 0;
else if (matrix_x >= static_cast<int32_t>(depth))
matrix_x = depth - 1;
if (matrix_y < 0)
matrix_y = 0;
else if (matrix_y >= static_cast<int32_t>(depth))
matrix_y = depth - 1;
*pixel = matrix.GetValue(matrix_x, matrix_y);
}
}
}
} // namespace
void DrawShadow(SkBitmap& image,
const gfx::Rect& shadow_rc,
const gfx::Rect& object_rc,
const gfx::Rect& clip_rc,
const ShadowMatrix& matrix) {
if (shadow_rc == object_rc)
return; // Nothing to paint.
// Fill top part.
gfx::Rect rc(shadow_rc.origin(),
gfx::Size(shadow_rc.width(), object_rc.y() - shadow_rc.y()));
PaintShadow(image, gfx::IntersectRects(rc, clip_rc), shadow_rc, matrix);
// Fill bottom part.
rc = gfx::Rect(shadow_rc.x(), object_rc.bottom(), shadow_rc.width(),
shadow_rc.bottom() - object_rc.bottom());
PaintShadow(image, gfx::IntersectRects(rc, clip_rc), shadow_rc, matrix);
// Fill left part.
rc = gfx::Rect(shadow_rc.x(), object_rc.y(), object_rc.x() - shadow_rc.x(),
object_rc.height());
PaintShadow(image, gfx::IntersectRects(rc, clip_rc), shadow_rc, matrix);
// Fill right part.
rc = gfx::Rect(object_rc.right(), object_rc.y(),
shadow_rc.right() - object_rc.right(), object_rc.height());
PaintShadow(image, gfx::IntersectRects(rc, clip_rc), shadow_rc, matrix);
}
} // namespace draw_utils
} // namespace chrome_pdf
|