mirror of https://github.com/aseprite/aseprite.git
				
				
				
			Add amount parameter to Error Diffusion algorithm
This commit is contained in:
		
							parent
							
								
									e1437a20c3
								
							
						
					
					
						commit
						fcf272bb69
					
				|  | @ -237,6 +237,7 @@ | |||
|     <section id="quantization"> | ||||
|       <option id="with_alpha" type="bool" default="true" /> | ||||
|       <option id="dithering_algorithm" type="std::string" /> | ||||
|       <option id="dithering_factor" type="int" default="100" /> | ||||
|     </section> | ||||
|     <section id="eyedropper" text="Editor"> | ||||
|       <option id="channel" type="EyedropperChannel" default="EyedropperChannel::COLOR_ALPHA" /> | ||||
|  |  | |||
|  | @ -482,6 +482,7 @@ delete = &Delete | |||
| 
 | ||||
| [color_mode] | ||||
| title = Color Mode | ||||
| amount = Amount: | ||||
| flatten = Merge layers | ||||
| 
 | ||||
| [convolution_matrix] | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| <!-- Aseprite --> | ||||
| <!-- Copyright (C) 2017 by David Capello --> | ||||
| <!-- Copyright (C) 2019  Igara Studio S.A. --> | ||||
| <!-- Copyright (C) 2017  David Capello --> | ||||
| <gui> | ||||
| <window id="color_mode" text="@.title"> | ||||
|   <vbox> | ||||
|  | @ -8,6 +9,11 @@ | |||
|     </view> | ||||
|     <slider min="0" max="100" id="progress" minwidth="100" /> | ||||
|     <hbox id="dithering_placeholder" /> | ||||
|     <hbox id="amount"> | ||||
|       <label text="@.amount" /> | ||||
|       <slider min="0" max="100" id="factor" minwidth="100" /> | ||||
|       <label text="%" /> | ||||
|     </hbox> | ||||
|     <check text="@.flatten" id="flatten" /> | ||||
|     <separator horizontal="true" /> | ||||
|     <hbox> | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| // Aseprite
 | ||||
| // Copyright (C) 2019  Igara Studio S.A.
 | ||||
| // Copyright (C) 2001-2018  David Capello
 | ||||
| //
 | ||||
| // This program is distributed under the terms of
 | ||||
|  | @ -69,6 +70,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite, | |||
|                                const PixelFormat newFormat, | ||||
|                                const render::DitheringAlgorithm ditheringAlgorithm, | ||||
|                                const render::DitheringMatrix& ditheringMatrix, | ||||
|                                const double ditheringFactor, | ||||
|                                render::TaskDelegate* delegate) | ||||
|   : WithSprite(sprite) | ||||
|   , m_oldFormat(sprite->pixelFormat()) | ||||
|  | @ -86,6 +88,7 @@ SetPixelFormat::SetPixelFormat(Sprite* sprite, | |||
|       (old_image.get(), nullptr, newFormat, | ||||
|        ditheringAlgorithm, | ||||
|        ditheringMatrix, | ||||
|        ditheringFactor, | ||||
|        sprite->rgbMap(cel->frame()), | ||||
|        sprite->palette(cel->frame()), | ||||
|        cel->layer()->isBackground(), | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| // Aseprite
 | ||||
| // Copyright (C) 2019  Igara Studio S.A.
 | ||||
| // Copyright (C) 2001-2017  David Capello
 | ||||
| //
 | ||||
| // This program is distributed under the terms of
 | ||||
|  | @ -32,6 +33,7 @@ namespace cmd { | |||
|                    const doc::PixelFormat newFormat, | ||||
|                    const render::DitheringAlgorithm ditheringAlgorithm, | ||||
|                    const render::DitheringMatrix& ditheringMatrix, | ||||
|                    const double ditheringFactor, | ||||
|                    render::TaskDelegate* delegate); | ||||
| 
 | ||||
|   protected: | ||||
|  |  | |||
|  | @ -79,6 +79,7 @@ public: | |||
|                 const doc::PixelFormat pixelFormat, | ||||
|                 const render::DitheringAlgorithm ditheringAlgorithm, | ||||
|                 const render::DitheringMatrix& ditheringMatrix, | ||||
|                 const double ditheringFactor, | ||||
|                 const gfx::Point& pos, | ||||
|                 const bool newBlend) | ||||
|     : m_image(dstImage) | ||||
|  | @ -92,11 +93,13 @@ public: | |||
|        pixelFormat, | ||||
|        ditheringAlgorithm, | ||||
|        ditheringMatrix, | ||||
|        ditheringFactor, | ||||
|        newBlend]() { // Copy the matrix
 | ||||
|         run(sprite, frame, | ||||
|             pixelFormat, | ||||
|             ditheringAlgorithm, | ||||
|             ditheringMatrix, | ||||
|             ditheringFactor, | ||||
|             newBlend); | ||||
|       }) | ||||
|   { | ||||
|  | @ -121,6 +124,7 @@ private: | |||
|            const doc::PixelFormat pixelFormat, | ||||
|            const render::DitheringAlgorithm ditheringAlgorithm, | ||||
|            const render::DitheringMatrix& ditheringMatrix, | ||||
|            const double ditheringFactor, | ||||
|            const bool newBlend) { | ||||
|     doc::ImageRef tmp( | ||||
|       Image::create(sprite->pixelFormat(), | ||||
|  | @ -142,6 +146,7 @@ private: | |||
|       pixelFormat, | ||||
|       ditheringAlgorithm, | ||||
|       ditheringMatrix, | ||||
|       ditheringFactor, | ||||
|       sprite->rgbMap(frame), | ||||
|       sprite->palette(frame), | ||||
|       (sprite->backgroundLayer() != nullptr), | ||||
|  | @ -208,6 +213,11 @@ public: | |||
|       m_ditheringSelector->Change.connect( | ||||
|         base::Bind<void>(&ColorModeWindow::onDithering, this)); | ||||
|       ditheringPlaceholder()->addChild(m_ditheringSelector); | ||||
| 
 | ||||
|       factor()->Change.connect(base::Bind<void>(&ColorModeWindow::onDithering, this)); | ||||
|     } | ||||
|     else { | ||||
|       amount()->setVisible(false); | ||||
|     } | ||||
|     if (from != IMAGE_GRAYSCALE) | ||||
|       colorMode()->addChild(new ConversionItem(IMAGE_GRAYSCALE)); | ||||
|  | @ -221,6 +231,9 @@ public: | |||
| 
 | ||||
|     progress()->setReadOnly(true); | ||||
| 
 | ||||
|     // Default dithering factor
 | ||||
|     factor()->setValue(Preferences::instance().quantization.ditheringFactor()); | ||||
| 
 | ||||
|     // Select first option
 | ||||
|     colorMode()->selectIndex(0); | ||||
|   } | ||||
|  | @ -244,6 +257,10 @@ public: | |||
|                                   render::BayerMatrix(8)); | ||||
|   } | ||||
| 
 | ||||
|   double ditheringFactor() const { | ||||
|     return double(factor()->getValue()) / 100.0; | ||||
|   } | ||||
| 
 | ||||
|   bool flattenEnabled() const { | ||||
|     return flatten()->isSelected(); | ||||
|   } | ||||
|  | @ -252,7 +269,14 @@ public: | |||
|   void saveDitheringAlgorithm() { | ||||
|     if (m_ditheringSelector) { | ||||
|       if (auto item = m_ditheringSelector->getSelectedItem()) { | ||||
|         Preferences::instance().quantization.ditheringAlgorithm(item->text()); | ||||
|         Preferences::instance().quantization.ditheringAlgorithm( | ||||
|           item->text()); | ||||
| 
 | ||||
|         if (m_ditheringSelector->ditheringAlgorithm() == | ||||
|             render::DitheringAlgorithm::ErrorDiffusion) { | ||||
|           Preferences::instance().quantization.ditheringFactor( | ||||
|             factor()->getValue()); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | @ -285,8 +309,15 @@ private: | |||
| 
 | ||||
|     doc::PixelFormat dstPixelFormat = item->pixelFormat(); | ||||
| 
 | ||||
|     if (m_ditheringSelector) | ||||
|       m_ditheringSelector->setVisible(dstPixelFormat == doc::IMAGE_INDEXED); | ||||
|     if (m_ditheringSelector) { | ||||
|       const bool toIndexed = (dstPixelFormat == doc::IMAGE_INDEXED); | ||||
|       m_ditheringSelector->setVisible(toIndexed); | ||||
| 
 | ||||
|       const bool errorDiff = | ||||
|         (m_ditheringSelector->ditheringAlgorithm() == | ||||
|          render::DitheringAlgorithm::ErrorDiffusion); | ||||
|       amount()->setVisible(toIndexed && errorDiff); | ||||
|     } | ||||
| 
 | ||||
|     m_image.reset( | ||||
|       Image::create(dstPixelFormat, | ||||
|  | @ -315,6 +346,7 @@ private: | |||
|         dstPixelFormat, | ||||
|         ditheringAlgorithm(), | ||||
|         ditheringMatrix(), | ||||
|         ditheringFactor(), | ||||
|         visibleBounds.origin(), | ||||
|         Preferences::instance().experimental.newBlend())); | ||||
| 
 | ||||
|  | @ -373,6 +405,7 @@ private: | |||
|   doc::PixelFormat m_format; | ||||
|   render::DitheringAlgorithm m_ditheringAlgorithm; | ||||
|   render::DitheringMatrix m_ditheringMatrix; | ||||
|   double m_ditheringFactor; | ||||
| }; | ||||
| 
 | ||||
| ChangePixelFormatCommand::ChangePixelFormatCommand() | ||||
|  | @ -381,6 +414,7 @@ ChangePixelFormatCommand::ChangePixelFormatCommand() | |||
|   m_useUI = true; | ||||
|   m_format = IMAGE_RGB; | ||||
|   m_ditheringAlgorithm = render::DitheringAlgorithm::None; | ||||
|   m_ditheringFactor = 1.0; | ||||
| } | ||||
| 
 | ||||
| void ChangePixelFormatCommand::onLoadParams(const Params& params) | ||||
|  | @ -483,6 +517,7 @@ void ChangePixelFormatCommand::onExecute(Context* context) | |||
|     m_format = window.pixelFormat(); | ||||
|     m_ditheringAlgorithm = window.ditheringAlgorithm(); | ||||
|     m_ditheringMatrix = window.ditheringMatrix(); | ||||
|     m_ditheringFactor = window.ditheringFactor(); | ||||
|     flatten = window.flattenEnabled(); | ||||
| 
 | ||||
|     window.saveDitheringAlgorithm(); | ||||
|  | @ -520,6 +555,7 @@ void ChangePixelFormatCommand::onExecute(Context* context) | |||
|             sprite, m_format, | ||||
|             m_ditheringAlgorithm, | ||||
|             m_ditheringMatrix, | ||||
|             m_ditheringFactor, | ||||
|             &job));             // SpriteJob is a render::TaskDelegate
 | ||||
|       }); | ||||
|     job.waitJob(); | ||||
|  |  | |||
|  | @ -283,7 +283,7 @@ void NewLayerCommand::onExecute(Context* context) | |||
|               nullptr, | ||||
|               sprite->pixelFormat(), | ||||
|               render::DitheringAlgorithm::None, | ||||
|               render::DitheringMatrix(), | ||||
|               render::DitheringMatrix(), 1.0, | ||||
|               sprite->rgbMap(dstFrame), | ||||
|               pasteSpr->palette(fr), | ||||
|               (pasteSpr->backgroundLayer() ? true: false), | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| // Aseprite
 | ||||
| // Copyright (C) 2019  Igara Studio S.A.
 | ||||
| // Copyright (C) 2001-2018  David Capello
 | ||||
| //
 | ||||
| // This program is distributed under the terms of
 | ||||
|  | @ -184,7 +185,7 @@ void PasteTextCommand::onExecute(Context* ctx) | |||
|           render::convert_pixel_format( | ||||
|             image.get(), NULL, sprite->pixelFormat(), | ||||
|             render::DitheringAlgorithm::None, | ||||
|             render::DitheringMatrix(), | ||||
|             render::DitheringMatrix(), 1.0, | ||||
|             rgbmap, sprite->palette(editor->frame()), | ||||
|             false, 0)); | ||||
|       } | ||||
|  |  | |||
|  | @ -744,7 +744,7 @@ void DocExporter::renderTexture(Context* ctx, const Samples& samples, Image* tex | |||
|         sample.sprite(), | ||||
|         textureImage->pixelFormat(), | ||||
|         render::DitheringAlgorithm::None, | ||||
|         render::DitheringMatrix(), | ||||
|         render::DitheringMatrix(), 1.0, | ||||
|         nullptr)                // TODO add a delegate to show progress
 | ||||
|         .execute(ctx); | ||||
|     } | ||||
|  |  | |||
|  | @ -746,7 +746,7 @@ private: | |||
|         render::convert_pixel_format | ||||
|         (oldImage, NULL, IMAGE_RGB, | ||||
|          render::DitheringAlgorithm::None, | ||||
|          render::DitheringMatrix(), | ||||
|          render::DitheringMatrix(), 1.0, | ||||
|          nullptr, | ||||
|          m_sprite->palette(cel->frame()), | ||||
|          m_opaque, | ||||
|  | @ -759,7 +759,7 @@ private: | |||
|       render::convert_pixel_format | ||||
|       (m_currentImage.get(), NULL, IMAGE_RGB, | ||||
|        render::DitheringAlgorithm::None, | ||||
|        render::DitheringMatrix(), | ||||
|        render::DitheringMatrix(), 1.0, | ||||
|        nullptr, | ||||
|        m_sprite->palette(m_frameNum), | ||||
|        m_opaque, | ||||
|  | @ -769,7 +769,7 @@ private: | |||
|       render::convert_pixel_format | ||||
|       (m_previousImage.get(), NULL, IMAGE_RGB, | ||||
|        render::DitheringAlgorithm::None, | ||||
|        render::DitheringMatrix(), | ||||
|        render::DitheringMatrix(), 1.0, | ||||
|        nullptr, | ||||
|        m_sprite->palette(MAX(0, m_frameNum-1)), | ||||
|        m_opaque, | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| // Aseprite
 | ||||
| // Copyright (C) 2019  Igara Studio S.A.
 | ||||
| // Copyright (C) 2017  David Capello
 | ||||
| //
 | ||||
| // This program is distributed under the terms of
 | ||||
|  | @ -101,7 +102,8 @@ private: | |||
|       doc::clear_image(image2.get(), 0); | ||||
|       render::convert_pixel_format( | ||||
|         image1.get(), image2.get(), IMAGE_INDEXED, | ||||
|         m_algo, m_matrix, nullptr, palette, true, -1, nullptr); | ||||
|         m_algo, m_matrix, 1.0, | ||||
|         nullptr, palette, true, -1, nullptr); | ||||
|     } | ||||
| 
 | ||||
|     m_preview = os::instance()->createRgbaSurface(w, h); | ||||
|  |  | |||
|  | @ -367,7 +367,7 @@ void paste() | |||
|           render::convert_pixel_format( | ||||
|             clipboard_image.get(), NULL, dstSpr->pixelFormat(), | ||||
|             render::DitheringAlgorithm::None, | ||||
|             render::DitheringMatrix(), | ||||
|             render::DitheringMatrix(), 1.0, | ||||
|             dst_rgbmap, clipboard_palette.get(), | ||||
|             false, | ||||
|             0)); | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| // Aseprite
 | ||||
| // Copyright (C) 2019  Igara Studio S.A.
 | ||||
| // Copyright (C) 2001-2018  David Capello
 | ||||
| //
 | ||||
| // This program is distributed under the terms of
 | ||||
|  | @ -54,7 +55,7 @@ Cel* create_cel_copy(const Cel* srcCel, | |||
|       tmpImage.get(), | ||||
|       IMAGE_RGB, | ||||
|       render::DitheringAlgorithm::None, | ||||
|       render::DitheringMatrix(), | ||||
|       render::DitheringMatrix(), 1.0, | ||||
|       srcCel->sprite()->rgbMap(srcCel->frame()), | ||||
|       srcCel->sprite()->palette(srcCel->frame()), | ||||
|       srcCel->layer()->isBackground(), | ||||
|  | @ -65,7 +66,7 @@ Cel* create_cel_copy(const Cel* srcCel, | |||
|       dstCel->image(), | ||||
|       IMAGE_INDEXED, | ||||
|       render::DitheringAlgorithm::None, | ||||
|       render::DitheringMatrix(), | ||||
|       render::DitheringMatrix(), 1.0, | ||||
|       dstSprite->rgbMap(dstFrame), | ||||
|       dstSprite->palette(dstFrame), | ||||
|       srcCel->layer()->isBackground(), | ||||
|  |  | |||
|  | @ -25,13 +25,15 @@ ErrorDiffusionDither::ErrorDiffusionDither(int transparentIndex) | |||
| 
 | ||||
| void ErrorDiffusionDither::start( | ||||
|   const doc::Image* srcImage, | ||||
|   doc::Image* dstImage) | ||||
|   doc::Image* dstImage, | ||||
|   const double factor) | ||||
| { | ||||
|   m_srcImage = srcImage; | ||||
|   m_width = 2+srcImage->width(); | ||||
|   for (int i=0; i<kChannels; ++i) | ||||
|     m_err[i].resize(m_width*2, 0); | ||||
|   m_lastY = -1; | ||||
|   m_factor = int(factor * 100.0); | ||||
| } | ||||
| 
 | ||||
| void ErrorDiffusionDither::finish() | ||||
|  | @ -89,11 +91,11 @@ doc::color_t ErrorDiffusionDither::ditherRgbToIndex2D( | |||
|   // TODO using Floyd-Steinberg matrix here but it should be configurable
 | ||||
|   for (int i=0; i<kChannels; ++i) { | ||||
|     int* err = &m_err[i][x]; | ||||
| 
 | ||||
|     const int a = quantError[i] * 7 / 16; | ||||
|     const int b = quantError[i] * 3 / 16; | ||||
|     const int c = quantError[i] * 5 / 16; | ||||
|     const int d = quantError[i] * 1 / 16; | ||||
|     const int q = quantError[i] * m_factor / 100; | ||||
|     const int a = q * 7 / 16; | ||||
|     const int b = q * 3 / 16; | ||||
|     const int c = q * 5 / 16; | ||||
|     const int d = q * 1 / 16; | ||||
| 
 | ||||
|     if (y & 1) { | ||||
|       err[0        ] += a; | ||||
|  |  | |||
|  | @ -23,7 +23,8 @@ namespace render { | |||
|     bool zigZag() const override { return true; } | ||||
|     void start( | ||||
|       const doc::Image* srcImage, | ||||
|       doc::Image* dstImage) override; | ||||
|       doc::Image* dstImage, | ||||
|       const double factor) override; | ||||
|     void finish() override; | ||||
|     doc::color_t ditherRgbToIndex2D( | ||||
|       const int x, const int y, | ||||
|  | @ -35,6 +36,7 @@ namespace render { | |||
|     int m_width, m_lastY; | ||||
|     static const int kChannels = 4; | ||||
|     std::vector<int> m_err[kChannels]; | ||||
|     int m_factor; | ||||
|   }; | ||||
| 
 | ||||
| } // namespace render
 | ||||
|  |  | |||
|  | @ -229,6 +229,7 @@ doc::color_t OrderedDither2::ditherRgbPixelToIndex( | |||
| void dither_rgb_image_to_indexed( | ||||
|   DitheringAlgorithmBase& algorithm, | ||||
|   const DitheringMatrix& matrix, | ||||
|   const double factor, | ||||
|   const doc::Image* srcImage, | ||||
|   doc::Image* dstImage, | ||||
|   const doc::RgbMap* rgbmap, | ||||
|  | @ -238,7 +239,7 @@ void dither_rgb_image_to_indexed( | |||
|   const int w = srcImage->width(); | ||||
|   const int h = srcImage->height(); | ||||
| 
 | ||||
|   algorithm.start(srcImage, dstImage); | ||||
|   algorithm.start(srcImage, dstImage, factor); | ||||
| 
 | ||||
|   if (algorithm.dimensions() == 1) { | ||||
|     const doc::LockImageBits<doc::RgbTraits> srcBits(srcImage); | ||||
|  |  | |||
|  | @ -29,7 +29,8 @@ namespace render { | |||
| 
 | ||||
|     virtual void start( | ||||
|       const doc::Image* srcImage, | ||||
|       doc::Image* dstImage) { } | ||||
|       doc::Image* dstImage, | ||||
|       const double factor) { } | ||||
| 
 | ||||
|     virtual void finish() { } | ||||
| 
 | ||||
|  | @ -77,6 +78,7 @@ namespace render { | |||
|   void dither_rgb_image_to_indexed( | ||||
|     DitheringAlgorithmBase& algorithm, | ||||
|     const DitheringMatrix& matrix, | ||||
|     const double factor, | ||||
|     const doc::Image* srcImage, | ||||
|     doc::Image* dstImage, | ||||
|     const doc::RgbMap* rgbmap, | ||||
|  |  | |||
|  | @ -86,6 +86,7 @@ Image* convert_pixel_format( | |||
|   PixelFormat pixelFormat, | ||||
|   DitheringAlgorithm ditheringAlgorithm, | ||||
|   const DitheringMatrix& ditheringMatrix, | ||||
|   const double ditheringFactor, | ||||
|   const RgbMap* rgbmap, | ||||
|   const Palette* palette, | ||||
|   bool is_background, | ||||
|  | @ -114,7 +115,8 @@ Image* convert_pixel_format( | |||
|     } | ||||
|     if (dither) | ||||
|       dither_rgb_image_to_indexed( | ||||
|         *dither, ditheringMatrix, image, new_image, rgbmap, palette, delegate); | ||||
|         *dither, ditheringMatrix, ditheringFactor, | ||||
|         image, new_image, rgbmap, palette, delegate); | ||||
|     return new_image; | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -55,6 +55,7 @@ namespace render { | |||
|     doc::PixelFormat pixelFormat, | ||||
|     render::DitheringAlgorithm ditheringAlgorithm, | ||||
|     const render::DitheringMatrix& ditheringMatrix, | ||||
|     const double ditheringFactor, | ||||
|     const doc::RgbMap* rgbmap, | ||||
|     const doc::Palette* palette, | ||||
|     bool is_background, | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue