File indexing completed on 2024-04-28 05:46:50

0001 /*****************************************************************************
0002  *   Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
0003  *                                                                           *
0004  *   This program is free software; you can redistribute it and/or modify    *
0005  *   it under the terms of the GNU Lesser General Public License as          *
0006  *   published by the Free Software Foundation; either version 2.1 of the    *
0007  *   License, or (at your option) version 3, or any later version accepted   *
0008  *   by the membership of KDE e.V. (or its successor approved by the         *
0009  *   membership of KDE e.V.), which shall act as a proxy defined in          *
0010  *   Section 6 of version 3 of the license.                                  *
0011  *                                                                           *
0012  *   This program is distributed in the hope that it will be useful,         *
0013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
0014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
0015  *   Lesser General Public License for more details.                         *
0016  *                                                                           *
0017  *   You should have received a copy of the GNU Lesser General Public        *
0018  *   License along with this library. If not,                                *
0019  *   see <http://www.gnu.org/licenses/>.                                     *
0020  *****************************************************************************/
0021 
0022 #include "shadow_p.h"
0023 #include "log.h"
0024 
0025 #include <cstdlib>
0026 
0027 static void
0028 qtcCreateShadowGradient(float *buff, size_t size)
0029 {
0030     const float r = size / 6.5;
0031     for (size_t i = 0;i < size;i++) {
0032         buff[i] = qtcMax(0, expf(-(i / r)) - 0.0015);
0033     }
0034 }
0035 
0036 static void
0037 qtcFillShadowPixel(uint8_t *pixel, const QtcColor *c1,
0038                    const QtcColor *c2, double bias, QtcPixelByteOrder order)
0039 {
0040     uint8_t alpha = qtcBound(0, 0xff * bias, 0xff);
0041     if (alpha == 0) {
0042         memset(pixel, 0, 4);
0043         return;
0044     }
0045     QtcColor color;
0046     // c1 is the start color, c2 the end color
0047     _qtcColorMix(c2, c1, bias, &color);
0048     uint8_t red = qtcBound(0, 0xff * color.red, 0xff) * alpha / 0xff;
0049     uint8_t green = qtcBound(0, 0xff * color.green, 0xff) * alpha / 0xff;
0050     uint8_t blue = qtcBound(0, 0xff * color.blue, 0xff) * alpha / 0xff;
0051     switch (order) {
0052     case QTC_PIXEL_ARGB:
0053         pixel[0] = alpha;
0054         pixel[1] = red;
0055         pixel[2] = green;
0056         pixel[3] = blue;
0057         break;
0058     case QTC_PIXEL_BGRA:
0059         pixel[0] = blue;
0060         pixel[1] = green;
0061         pixel[2] = red;
0062         pixel[3] = alpha;
0063         break;
0064     default:
0065     case QTC_PIXEL_RGBA:
0066         pixel[0] = red;
0067         pixel[1] = green;
0068         pixel[2] = blue;
0069         pixel[3] = alpha;
0070         break;
0071     }
0072 }
0073 
0074 static inline float
0075 _qtcDistance(int x, int y, int x0, int y0, bool square)
0076 {
0077     int dx = x - x0;
0078     int dy = y - y0;
0079     if (dx == 0) {
0080         return std::abs(dy);
0081     }
0082     if (dy == 0) {
0083         return std::abs(dx);
0084     }
0085     return (square ? qtcMax(std::abs(dx), std::abs(dy)) :
0086             sqrtf(dx * dx + dy * dy));
0087 }
0088 
0089 static inline float
0090 _qtcGradientGetValue(float *gradient, size_t size, float distance)
0091 {
0092     if (distance < 0 || distance > size - 1) {
0093         return 0;
0094     }
0095     int index = floorf(distance);
0096     if (qtcEqual(index, distance)) {
0097         return gradient[index];
0098     }
0099     return (gradient[index] * (index + 1 - distance) +
0100             gradient[index + 1] * (distance - index));
0101 }
0102 
0103 static QtCurve::Image*
0104 qtcShadowSubImage(size_t size, float *gradient, int vertical_align,
0105                   int horizontal_align, const QtcColor *c1, const QtcColor *c2,
0106                   bool square, QtcPixelByteOrder order)
0107 {
0108     int height = vertical_align ? size : 1;
0109     int y0 = vertical_align == -1 ? height - 1 : 0;
0110     int width = horizontal_align ? size : 1;
0111     int x0 = horizontal_align == -1 ? width - 1 : 0;
0112     auto *res = new QtCurve::Image(width, height, 4);
0113     for (int x = 0;x < width;x++) {
0114         for (int y = 0;y < height;y++) {
0115             qtcFillShadowPixel(
0116                 &res->data[(x + y * width) * 4], c1, c2,
0117                 _qtcGradientGetValue(
0118                     gradient, size, _qtcDistance(x, y, x0, y0, square)), order);
0119         }
0120     }
0121     return res;
0122 }
0123 
0124 void
0125 qtcShadowCreate(size_t size, const QtcColor *c1, const QtcColor *c2,
0126                 size_t radius, bool square, QtcPixelByteOrder order,
0127                 QtCurve::Image **images)
0128 {
0129     size_t full_size = size + radius;
0130     QtCurve::LocalBuff<float, 128> gradient(full_size);
0131     for (size_t i = 0;i < radius;i++) {
0132         gradient[i] = 0;
0133     }
0134     qtcCreateShadowGradient(gradient.get() + radius, size);
0135     int aligns[8][2] = {
0136         {0, -1},
0137         {1, -1},
0138         {1, 0},
0139         {1, 1},
0140         {0, 1},
0141         {-1, 1},
0142         {-1, 0},
0143         {-1, -1},
0144     };
0145     for (int i = 0;i < 8;i++) {
0146         images[i] = qtcShadowSubImage(full_size, gradient.get(), aligns[i][1],
0147                                       aligns[i][0], c1, c2, square, order);
0148     }
0149 }