File indexing completed on 2024-12-22 04:15:36

0001 /*
0002  * KDE. Krita Project.
0003  *
0004  * SPDX-FileCopyrightText: 2019 Eoin O 'Neill <eoinoneill1991@gmail.com>
0005  * SPDX-FileCopyrightText: 2019 Emmet O 'Neill <emmetoneill.pdx@gmail.com>
0006  *
0007  *  SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 
0010 #include "simplexnoisegenerator.h"
0011 #include "ui_wdgsimplexnoiseoptions.h"
0012 #include "kis_wdg_simplex_noise.h"
0013 #include "3rdparty/c-open-simplex/open-simplex-noise.h"
0014 
0015 #include <KisSequentialIteratorProgress.h>
0016 #include <KoUpdater.h>
0017 #include <QCryptographicHash>
0018 #include <filter/kis_filter_configuration.h>
0019 #include <generator/kis_generator_registry.h>
0020 #include <KoColorModelStandardIds.h>
0021 #include <KoColorSpaceRegistry.h>
0022 #include <kis_processing_information.h>
0023 #include <kpluginfactory.h>
0024 
0025 K_PLUGIN_FACTORY_WITH_JSON(KritaSimplexNoiseGeneratorFactory, "kritasimplexnoisegenerator.json", registerPlugin<KisSimplexNoiseGeneratorHandle>();)
0026 
0027 KisSimplexNoiseGeneratorHandle::KisSimplexNoiseGeneratorHandle(QObject *parent, const QVariantList &)
0028         : QObject(parent)
0029 {
0030     KisGeneratorRegistry::instance()->add(new KisSimplexNoiseGenerator());
0031 
0032 }
0033 
0034 KisSimplexNoiseGeneratorHandle::~KisSimplexNoiseGeneratorHandle()
0035 {
0036 }
0037 
0038 KisSimplexNoiseGenerator::KisSimplexNoiseGenerator() : KisGenerator(id(), KoID("basic"), i18n("&Simplex Noise..."))
0039 {
0040     setColorSpaceIndependence(FULLY_INDEPENDENT);
0041     setSupportsPainting(true);
0042 }
0043 
0044 void KisSimplexNoiseGenerator::generate(KisProcessingInformation dst, const QSize &size, const KisFilterConfigurationSP config, KoUpdater *progressUpdater) const
0045 {
0046     KisPaintDeviceSP device = dst.paintDevice();
0047     Q_ASSERT(!device.isNull());
0048 
0049     osn_context *noise_context;
0050 
0051     QRect bounds = QRect(dst.topLeft(), size);
0052     QRect whole_image_bounds = device->defaultBounds()->bounds();
0053 
0054     const KoColorSpace *cs = device->colorSpace();
0055     const KoColorSpace *src = KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Float32BitsColorDepthID.id(), "Gray-D50-elle-V2-srgbtrc.icc");
0056     KoColorConversionTransformation *conv = KoColorSpaceRegistry::instance()->createColorConverter(src, cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
0057 
0058     KisSequentialIteratorProgress it(device, bounds, progressUpdater);
0059 
0060     QVariant property;
0061 
0062     const uint default_seed = (config->getProperty("seed", property)) ? property.toUInt() : 0;
0063     const QString custom_seed_string = (config->getProperty("custom_seed_string", property)) ? property.toString() : "";
0064     const bool use_custom_seed = !custom_seed_string.trimmed().isEmpty();
0065 
0066     const uint seed = use_custom_seed ? seedFromString(custom_seed_string) : default_seed;
0067     open_simplex_noise(seed, &noise_context);
0068 
0069     double frequency = (config && config->getProperty("frequency", property)) ? property.toDouble() : 25.0;
0070     double ratio_x = (config && config->getProperty("ratio_x", property)) ? property.toDouble() : 1.0;
0071     double ratio_y = (config && config->getProperty("ratio_y", property)) ? property.toDouble() : 1.0;
0072 
0073     bool looping = (config && config->getProperty("looping", property)) ? property.toBool() : false;
0074 
0075     if( looping ){
0076         float major_radius = 0.5f * frequency * ratio_x;
0077         float minor_radius = 0.5f * frequency * ratio_y;
0078         while(it.nextPixel()){
0079             double x_phase = (double)it.x() / (double)whole_image_bounds.width() * M_PI * 2;
0080             double y_phase = (double)it.y() / (double)(whole_image_bounds.height()) * M_PI * 2;
0081             double x_coordinate = major_radius * map_range(cos(x_phase), -1.0, 1.0, 0.0, 1.0);
0082             double y_coordinate = major_radius * map_range(sin(x_phase), -1.0, 1.0, 0.0, 1.0);
0083             double z_coordinate = minor_radius * map_range(cos(y_phase), -1.0, 1.0, 0.0, 1.0);
0084             double w_coordinate = minor_radius * map_range(sin(y_phase), -1.0, 1.0, 0.0, 1.0);
0085             double value = open_simplex_noise4(noise_context, x_coordinate, y_coordinate, z_coordinate, w_coordinate);
0086             value = map_range(value, -1.0, 1.0, 0.0, 1.0);
0087 
0088             KoColor c(src);
0089             reinterpret_cast<float *>(c.data())[0] = value;
0090             c.setOpacity(OPACITY_OPAQUE_F);
0091 
0092             conv->transform(c.data(), it.rawData(), 1);
0093         }
0094     } else {
0095         while(it.nextPixel()){
0096             double x_phase = (double)it.x() / (double)(whole_image_bounds.width()) * ratio_x;
0097             double y_phase = (double)it.y() / (double)(whole_image_bounds.height()) * ratio_y;
0098             double value = open_simplex_noise4(noise_context, x_phase * frequency, y_phase * frequency, x_phase * frequency, y_phase * frequency);
0099             value = map_range(value, -1.0, 1.0, 0.0, 1.0);
0100 
0101             KoColor c(src);
0102             reinterpret_cast<float *>(c.data())[0] = value;
0103             c.setOpacity(OPACITY_OPAQUE_F);
0104 
0105             conv->transform(c.data(), it.rawData(), 1);
0106         }
0107     }
0108     delete conv;
0109     open_simplex_noise_free(noise_context);
0110 }
0111 
0112 KisFilterConfigurationSP KisSimplexNoiseGenerator::defaultConfiguration(KisResourcesInterfaceSP resourcesInterface) const
0113 {
0114     KisFilterConfigurationSP config = factoryConfiguration(resourcesInterface);
0115     config->setProperty("looping", false);
0116     config->setProperty("frequency", 25.0);
0117     uint seed = static_cast<uint>(rand());
0118     config->setProperty("seed", seed);
0119     config->setProperty("custom_seed_string", "");
0120     config->setProperty("ratio_x", 1.0f);
0121     config->setProperty("ratio_y", 1.0f);
0122     return config;
0123 }
0124 
0125 KisConfigWidget * KisSimplexNoiseGenerator::createConfigurationWidget(QWidget* parent, const KisPaintDeviceSP dev, bool) const
0126 {
0127     Q_UNUSED(dev);
0128     return new KisWdgSimplexNoise((KisFilter*)this, (QWidget*)parent);
0129 }
0130 
0131 
0132 uint KisSimplexNoiseGenerator::seedFromString(const QString &string) const
0133 {
0134     QByteArray bytes = QCryptographicHash::hash(string.toUtf8(),QCryptographicHash::Md5);
0135     uint hash = 0;
0136     for( int index = 0; index < bytes.length(); index++){
0137         hash += rotateLeft(bytes[index], index % 32);
0138     }
0139     return hash;
0140 }
0141 
0142 quint64 KisSimplexNoiseGenerator::rotateLeft(const quint64 input, uint shift) const
0143 {
0144     return (input << shift)|(input >> (64 - shift));
0145 }
0146 
0147 #include "simplexnoisegenerator.moc"
0148