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