1 /*****************************************************************************
2 * Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU Lesser General Public License as *
6 * published by the Free Software Foundation; either version 2.1 of the *
7 * License, or (at your option) version 3, or any later version accepted *
8 * by the membership of KDE e.V. (or its successor approved by the *
9 * membership of KDE e.V.), which shall act as a proxy defined in *
10 * Section 6 of version 3 of the license. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
15 * Lesser General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with this library. If not, *
19 * see <http://www.gnu.org/licenses/>. *
20 *****************************************************************************/
21
22 #include "shadow_p.h"
23 #include "log.h"
24
25 #include <cstdlib>
26
27 static void
qtcCreateShadowGradient(float * buff,size_t size)28 qtcCreateShadowGradient(float *buff, size_t size)
29 {
30 const float r = size / 6.5;
31 for (size_t i = 0;i < size;i++) {
32 buff[i] = qtcMax(0, expf(-(i / r)) - 0.0015);
33 }
34 }
35
36 static void
qtcFillShadowPixel(uint8_t * pixel,const QtcColor * c1,const QtcColor * c2,double bias,QtcPixelByteOrder order)37 qtcFillShadowPixel(uint8_t *pixel, const QtcColor *c1,
38 const QtcColor *c2, double bias, QtcPixelByteOrder order)
39 {
40 uint8_t alpha = qtcBound(0, 0xff * bias, 0xff);
41 if (alpha == 0) {
42 memset(pixel, 0, 4);
43 return;
44 }
45 QtcColor color;
46 // c1 is the start color, c2 the end color
47 _qtcColorMix(c2, c1, bias, &color);
48 uint8_t red = qtcBound(0, 0xff * color.red, 0xff) * alpha / 0xff;
49 uint8_t green = qtcBound(0, 0xff * color.green, 0xff) * alpha / 0xff;
50 uint8_t blue = qtcBound(0, 0xff * color.blue, 0xff) * alpha / 0xff;
51 switch (order) {
52 case QTC_PIXEL_ARGB:
53 pixel[0] = alpha;
54 pixel[1] = red;
55 pixel[2] = green;
56 pixel[3] = blue;
57 break;
58 case QTC_PIXEL_BGRA:
59 pixel[0] = blue;
60 pixel[1] = green;
61 pixel[2] = red;
62 pixel[3] = alpha;
63 break;
64 default:
65 case QTC_PIXEL_RGBA:
66 pixel[0] = red;
67 pixel[1] = green;
68 pixel[2] = blue;
69 pixel[3] = alpha;
70 break;
71 }
72 }
73
74 static inline float
_qtcDistance(int x,int y,int x0,int y0,bool square)75 _qtcDistance(int x, int y, int x0, int y0, bool square)
76 {
77 int dx = x - x0;
78 int dy = y - y0;
79 if (dx == 0) {
80 return std::abs(dy);
81 }
82 if (dy == 0) {
83 return std::abs(dx);
84 }
85 return (square ? qtcMax(std::abs(dx), std::abs(dy)) :
86 sqrtf(dx * dx + dy * dy));
87 }
88
89 static inline float
_qtcGradientGetValue(float * gradient,size_t size,float distance)90 _qtcGradientGetValue(float *gradient, size_t size, float distance)
91 {
92 if (distance < 0 || distance > size - 1) {
93 return 0;
94 }
95 int index = floorf(distance);
96 if (qtcEqual(index, distance)) {
97 return gradient[index];
98 }
99 return (gradient[index] * (index + 1 - distance) +
100 gradient[index + 1] * (distance - index));
101 }
102
103 static QtCurve::Image*
qtcShadowSubImage(size_t size,float * gradient,int vertical_align,int horizontal_align,const QtcColor * c1,const QtcColor * c2,bool square,QtcPixelByteOrder order)104 qtcShadowSubImage(size_t size, float *gradient, int vertical_align,
105 int horizontal_align, const QtcColor *c1, const QtcColor *c2,
106 bool square, QtcPixelByteOrder order)
107 {
108 int height = vertical_align ? size : 1;
109 int y0 = vertical_align == -1 ? height - 1 : 0;
110 int width = horizontal_align ? size : 1;
111 int x0 = horizontal_align == -1 ? width - 1 : 0;
112 auto *res = new QtCurve::Image(width, height, 4);
113 for (int x = 0;x < width;x++) {
114 for (int y = 0;y < height;y++) {
115 qtcFillShadowPixel(
116 &res->data[(x + y * width) * 4], c1, c2,
117 _qtcGradientGetValue(
118 gradient, size, _qtcDistance(x, y, x0, y0, square)), order);
119 }
120 }
121 return res;
122 }
123
124 void
qtcShadowCreate(size_t size,const QtcColor * c1,const QtcColor * c2,size_t radius,bool square,QtcPixelByteOrder order,QtCurve::Image ** images)125 qtcShadowCreate(size_t size, const QtcColor *c1, const QtcColor *c2,
126 size_t radius, bool square, QtcPixelByteOrder order,
127 QtCurve::Image **images)
128 {
129 size_t full_size = size + radius;
130 QtCurve::LocalBuff<float, 128> gradient(full_size);
131 for (size_t i = 0;i < radius;i++) {
132 gradient[i] = 0;
133 }
134 qtcCreateShadowGradient(gradient.get() + radius, size);
135 int aligns[8][2] = {
136 {0, -1},
137 {1, -1},
138 {1, 0},
139 {1, 1},
140 {0, 1},
141 {-1, 1},
142 {-1, 0},
143 {-1, -1},
144 };
145 for (int i = 0;i < 8;i++) {
146 images[i] = qtcShadowSubImage(full_size, gradient.get(), aligns[i][1],
147 aligns[i][0], c1, c2, square, order);
148 }
149 }
150