From 8b78a8791bc539bcffe7159f9d9714d577cb3d7d Mon Sep 17 00:00:00 2001 From: Michele Calgaro Date: Sun, 23 May 2021 20:48:35 +0900 Subject: Renaming of files in preparation for code style tools. Signed-off-by: Michele Calgaro --- chalk/core/Makefile.am | 28 +- chalk/core/createdcop.py | 2 +- chalk/core/kis_adjustment_layer.cc | 252 --- chalk/core/kis_adjustment_layer.cpp | 252 +++ chalk/core/kis_alpha_mask.cc | 132 -- chalk/core/kis_alpha_mask.cpp | 132 ++ chalk/core/kis_autobrush_resource.cc | 106 -- chalk/core/kis_autobrush_resource.cpp | 106 ++ chalk/core/kis_autogradient_resource.cc | 221 --- chalk/core/kis_autogradient_resource.cpp | 221 +++ chalk/core/kis_background.cc | 155 -- chalk/core/kis_background.cpp | 155 ++ chalk/core/kis_boundary.cc | 83 - chalk/core/kis_boundary.cpp | 83 + chalk/core/kis_brush.cc | 1333 --------------- chalk/core/kis_brush.cpp | 1333 +++++++++++++++ chalk/core/kis_command.cc | 43 - chalk/core/kis_command.cpp | 43 + chalk/core/kis_convolution_painter.cc | 426 ----- chalk/core/kis_convolution_painter.cpp | 426 +++++ chalk/core/kis_exif_info.cc | 66 - chalk/core/kis_exif_info.cpp | 66 + chalk/core/kis_exif_value.cc | 689 -------- chalk/core/kis_exif_value.cpp | 689 ++++++++ chalk/core/kis_fill_painter.cc | 407 ----- chalk/core/kis_fill_painter.cpp | 407 +++++ chalk/core/kis_filter.cc | 133 -- chalk/core/kis_filter.cpp | 133 ++ chalk/core/kis_filter_config_widget.cc | 31 - chalk/core/kis_filter_config_widget.cpp | 31 + chalk/core/kis_filter_configuration.cc | 185 --- chalk/core/kis_filter_configuration.cpp | 185 +++ chalk/core/kis_filter_registry.cc | 82 - chalk/core/kis_filter_registry.cpp | 82 + chalk/core/kis_filter_strategy.cc | 192 --- chalk/core/kis_filter_strategy.cpp | 192 +++ chalk/core/kis_gradient.cc | 639 -------- chalk/core/kis_gradient.cpp | 639 ++++++++ chalk/core/kis_gradient_painter.cc | 723 --------- chalk/core/kis_gradient_painter.cpp | 723 +++++++++ chalk/core/kis_group_layer.cc | 428 ----- chalk/core/kis_group_layer.cpp | 428 +++++ chalk/core/kis_histogram.cc | 215 --- chalk/core/kis_histogram.cpp | 215 +++ chalk/core/kis_image.cc | 1702 -------------------- chalk/core/kis_image.cpp | 1702 ++++++++++++++++++++ chalk/core/kis_image_iface.cc | 97 -- chalk/core/kis_image_iface.cpp | 97 ++ chalk/core/kis_imagepipe_brush.cc | 456 ------ chalk/core/kis_imagepipe_brush.cpp | 456 ++++++ chalk/core/kis_iterator.cc | 142 -- chalk/core/kis_iterator.cpp | 142 ++ chalk/core/kis_iterators_pixel.cc | 59 - chalk/core/kis_iterators_pixel.cpp | 59 + chalk/core/kis_layer.cc | 611 ------- chalk/core/kis_layer.cpp | 611 +++++++ chalk/core/kis_meta_registry.cc | 68 - chalk/core/kis_meta_registry.cpp | 68 + chalk/core/kis_nameserver.cc | 49 - chalk/core/kis_nameserver.cpp | 49 + chalk/core/kis_paint_device.cc | 1285 --------------- chalk/core/kis_paint_device.cpp | 1285 +++++++++++++++ chalk/core/kis_paint_device_iface.cc | 74 - chalk/core/kis_paint_device_iface.cpp | 74 + chalk/core/kis_paint_layer.cc | 509 ------ chalk/core/kis_paint_layer.cpp | 509 ++++++ chalk/core/kis_painter.cc | 928 ----------- chalk/core/kis_painter.cpp | 928 +++++++++++ chalk/core/kis_paintop.cc | 113 -- chalk/core/kis_paintop.cpp | 113 ++ chalk/core/kis_paintop_registry.cc | 139 -- chalk/core/kis_paintop_registry.cpp | 139 ++ chalk/core/kis_palette.cc | 306 ---- chalk/core/kis_palette.cpp | 306 ++++ chalk/core/kis_pattern.cc | 335 ---- chalk/core/kis_pattern.cpp | 335 ++++ chalk/core/kis_rect.cc | 28 - chalk/core/kis_rect.cpp | 28 + chalk/core/kis_resource.cc | 62 - chalk/core/kis_resource.cpp | 62 + chalk/core/kis_rotate_visitor.cc | 406 ----- chalk/core/kis_rotate_visitor.cpp | 406 +++++ chalk/core/kis_scale_visitor.cc | 279 ---- chalk/core/kis_scale_visitor.cpp | 279 ++++ chalk/core/kis_selected_transaction.cc | 73 - chalk/core/kis_selected_transaction.cpp | 73 + chalk/core/kis_selection.cc | 582 ------- chalk/core/kis_selection.cpp | 582 +++++++ chalk/core/kis_strategy_move.cc | 148 -- chalk/core/kis_strategy_move.cpp | 148 ++ chalk/core/kis_thread_pool.cc | 192 --- chalk/core/kis_thread_pool.cpp | 192 +++ chalk/core/kis_transaction.cc | 94 -- chalk/core/kis_transaction.cpp | 94 ++ chalk/core/kis_transform_worker.cc | 676 -------- chalk/core/kis_transform_worker.cpp | 676 ++++++++ chalk/core/kis_vec.cc | 67 - chalk/core/kis_vec.cpp | 67 + chalk/core/tests/Makefile.am | 2 +- .../core/tests/kis_filter_configuration_tester.cc | 67 - .../core/tests/kis_filter_configuration_tester.cpp | 67 + chalk/core/tiles/Makefile.am | 6 +- chalk/core/tiles/kis_memento.cc | 154 -- chalk/core/tiles/kis_memento.cpp | 154 ++ chalk/core/tiles/kis_tile.cc | 152 -- chalk/core/tiles/kis_tile.cpp | 152 ++ chalk/core/tiles/kis_tiled_random_accessor.cc | 115 -- chalk/core/tiles/kis_tiled_random_accessor.cpp | 115 ++ chalk/core/tiles/kis_tileddatamanager.cc | 1044 ------------ chalk/core/tiles/kis_tileddatamanager.cpp | 1044 ++++++++++++ chalk/core/tiles/kis_tiledhlineiterator.cc | 213 --- chalk/core/tiles/kis_tiledhlineiterator.cpp | 213 +++ chalk/core/tiles/kis_tilediterator.cc | 131 -- chalk/core/tiles/kis_tilediterator.cpp | 131 ++ chalk/core/tiles/kis_tiledrectiterator.cc | 242 --- chalk/core/tiles/kis_tiledrectiterator.cpp | 242 +++ chalk/core/tiles/kis_tiledvlineiterator.cc | 154 -- chalk/core/tiles/kis_tiledvlineiterator.cpp | 154 ++ chalk/core/tiles/kis_tilemanager.cc | 578 ------- chalk/core/tiles/kis_tilemanager.cpp | 578 +++++++ 120 files changed, 18890 insertions(+), 18890 deletions(-) delete mode 100644 chalk/core/kis_adjustment_layer.cc create mode 100644 chalk/core/kis_adjustment_layer.cpp delete mode 100644 chalk/core/kis_alpha_mask.cc create mode 100644 chalk/core/kis_alpha_mask.cpp delete mode 100644 chalk/core/kis_autobrush_resource.cc create mode 100644 chalk/core/kis_autobrush_resource.cpp delete mode 100644 chalk/core/kis_autogradient_resource.cc create mode 100644 chalk/core/kis_autogradient_resource.cpp delete mode 100644 chalk/core/kis_background.cc create mode 100644 chalk/core/kis_background.cpp delete mode 100644 chalk/core/kis_boundary.cc create mode 100644 chalk/core/kis_boundary.cpp delete mode 100644 chalk/core/kis_brush.cc create mode 100644 chalk/core/kis_brush.cpp delete mode 100644 chalk/core/kis_command.cc create mode 100644 chalk/core/kis_command.cpp delete mode 100644 chalk/core/kis_convolution_painter.cc create mode 100644 chalk/core/kis_convolution_painter.cpp delete mode 100644 chalk/core/kis_exif_info.cc create mode 100644 chalk/core/kis_exif_info.cpp delete mode 100644 chalk/core/kis_exif_value.cc create mode 100644 chalk/core/kis_exif_value.cpp delete mode 100644 chalk/core/kis_fill_painter.cc create mode 100644 chalk/core/kis_fill_painter.cpp delete mode 100644 chalk/core/kis_filter.cc create mode 100644 chalk/core/kis_filter.cpp delete mode 100644 chalk/core/kis_filter_config_widget.cc create mode 100644 chalk/core/kis_filter_config_widget.cpp delete mode 100644 chalk/core/kis_filter_configuration.cc create mode 100644 chalk/core/kis_filter_configuration.cpp delete mode 100644 chalk/core/kis_filter_registry.cc create mode 100644 chalk/core/kis_filter_registry.cpp delete mode 100644 chalk/core/kis_filter_strategy.cc create mode 100644 chalk/core/kis_filter_strategy.cpp delete mode 100644 chalk/core/kis_gradient.cc create mode 100644 chalk/core/kis_gradient.cpp delete mode 100644 chalk/core/kis_gradient_painter.cc create mode 100644 chalk/core/kis_gradient_painter.cpp delete mode 100644 chalk/core/kis_group_layer.cc create mode 100644 chalk/core/kis_group_layer.cpp delete mode 100644 chalk/core/kis_histogram.cc create mode 100644 chalk/core/kis_histogram.cpp delete mode 100644 chalk/core/kis_image.cc create mode 100644 chalk/core/kis_image.cpp delete mode 100644 chalk/core/kis_image_iface.cc create mode 100644 chalk/core/kis_image_iface.cpp delete mode 100644 chalk/core/kis_imagepipe_brush.cc create mode 100644 chalk/core/kis_imagepipe_brush.cpp delete mode 100644 chalk/core/kis_iterator.cc create mode 100644 chalk/core/kis_iterator.cpp delete mode 100644 chalk/core/kis_iterators_pixel.cc create mode 100644 chalk/core/kis_iterators_pixel.cpp delete mode 100644 chalk/core/kis_layer.cc create mode 100644 chalk/core/kis_layer.cpp delete mode 100644 chalk/core/kis_meta_registry.cc create mode 100644 chalk/core/kis_meta_registry.cpp delete mode 100644 chalk/core/kis_nameserver.cc create mode 100644 chalk/core/kis_nameserver.cpp delete mode 100644 chalk/core/kis_paint_device.cc create mode 100644 chalk/core/kis_paint_device.cpp delete mode 100644 chalk/core/kis_paint_device_iface.cc create mode 100644 chalk/core/kis_paint_device_iface.cpp delete mode 100644 chalk/core/kis_paint_layer.cc create mode 100644 chalk/core/kis_paint_layer.cpp delete mode 100644 chalk/core/kis_painter.cc create mode 100644 chalk/core/kis_painter.cpp delete mode 100644 chalk/core/kis_paintop.cc create mode 100644 chalk/core/kis_paintop.cpp delete mode 100644 chalk/core/kis_paintop_registry.cc create mode 100644 chalk/core/kis_paintop_registry.cpp delete mode 100644 chalk/core/kis_palette.cc create mode 100644 chalk/core/kis_palette.cpp delete mode 100644 chalk/core/kis_pattern.cc create mode 100644 chalk/core/kis_pattern.cpp delete mode 100644 chalk/core/kis_rect.cc create mode 100644 chalk/core/kis_rect.cpp delete mode 100644 chalk/core/kis_resource.cc create mode 100644 chalk/core/kis_resource.cpp delete mode 100644 chalk/core/kis_rotate_visitor.cc create mode 100644 chalk/core/kis_rotate_visitor.cpp delete mode 100644 chalk/core/kis_scale_visitor.cc create mode 100644 chalk/core/kis_scale_visitor.cpp delete mode 100644 chalk/core/kis_selected_transaction.cc create mode 100644 chalk/core/kis_selected_transaction.cpp delete mode 100644 chalk/core/kis_selection.cc create mode 100644 chalk/core/kis_selection.cpp delete mode 100644 chalk/core/kis_strategy_move.cc create mode 100644 chalk/core/kis_strategy_move.cpp delete mode 100644 chalk/core/kis_thread_pool.cc create mode 100644 chalk/core/kis_thread_pool.cpp delete mode 100644 chalk/core/kis_transaction.cc create mode 100644 chalk/core/kis_transaction.cpp delete mode 100644 chalk/core/kis_transform_worker.cc create mode 100644 chalk/core/kis_transform_worker.cpp delete mode 100644 chalk/core/kis_vec.cc create mode 100644 chalk/core/kis_vec.cpp delete mode 100644 chalk/core/tests/kis_filter_configuration_tester.cc create mode 100644 chalk/core/tests/kis_filter_configuration_tester.cpp delete mode 100644 chalk/core/tiles/kis_memento.cc create mode 100644 chalk/core/tiles/kis_memento.cpp delete mode 100644 chalk/core/tiles/kis_tile.cc create mode 100644 chalk/core/tiles/kis_tile.cpp delete mode 100644 chalk/core/tiles/kis_tiled_random_accessor.cc create mode 100644 chalk/core/tiles/kis_tiled_random_accessor.cpp delete mode 100644 chalk/core/tiles/kis_tileddatamanager.cc create mode 100644 chalk/core/tiles/kis_tileddatamanager.cpp delete mode 100644 chalk/core/tiles/kis_tiledhlineiterator.cc create mode 100644 chalk/core/tiles/kis_tiledhlineiterator.cpp delete mode 100644 chalk/core/tiles/kis_tilediterator.cc create mode 100644 chalk/core/tiles/kis_tilediterator.cpp delete mode 100644 chalk/core/tiles/kis_tiledrectiterator.cc create mode 100644 chalk/core/tiles/kis_tiledrectiterator.cpp delete mode 100644 chalk/core/tiles/kis_tiledvlineiterator.cc create mode 100644 chalk/core/tiles/kis_tiledvlineiterator.cpp delete mode 100644 chalk/core/tiles/kis_tilemanager.cc create mode 100644 chalk/core/tiles/kis_tilemanager.cpp (limited to 'chalk/core') diff --git a/chalk/core/Makefile.am b/chalk/core/Makefile.am index 9370161a..ae67f2c0 100644 --- a/chalk/core/Makefile.am +++ b/chalk/core/Makefile.am @@ -13,21 +13,21 @@ CXXFLAGS += $(OPENEXR_CXXFLAGS) lib_LTLIBRARIES = libchalkimage.la -libchalkimage_la_SOURCES = kis_adjustment_layer.cc kis_alpha_mask.cc \ - kis_autobrush_resource.cc kis_autogradient_resource.cc kis_background.cc kis_boundary.cc \ - kis_brush.cc kis_command.cc kis_convolution_painter.cc kis_fill_painter.cc \ - kis_filter.cc kis_filter_registry.cc kis_filter_strategy.cc \ - kis_filter_configuration.cc kis_filter_config_widget.cc kis_gradient.cc kis_gradient_painter.cc \ - kis_histogram.cc kis_image.cc kis_imagepipe_brush.cc kis_iterator.cc \ - kis_iterators_pixel.cc kis_layer.cc kis_group_layer.cc kis_paint_layer.cc kis_meta_registry.cc \ - kis_nameserver.cc kis_painter.cc kis_paintop.cc kis_paintop_registry.cc kis_palette.cc \ - kis_pattern.cc kis_rect.cc kis_resource.cc kis_rotate_visitor.cc \ - kis_selected_transaction.cc kis_selection.cc kis_strategy_move.cc kis_transaction.cc \ - kis_transform_worker.cc kis_vec.cc kis_paint_device.cc kis_paint_device_iface.cc \ - kis_paint_device_iface.skel kis_image_iface.cc kis_image_iface.skel kis_basic_math_toolbox.cpp \ - kis_math_toolbox.cpp kis_exif_info.cc kis_thread_pool.cc kis_exif_value.cc \ +libchalkimage_la_SOURCES = kis_adjustment_layer.cpp kis_alpha_mask.cpp \ + kis_autobrush_resource.cpp kis_autogradient_resource.cpp kis_background.cpp kis_boundary.cpp \ + kis_brush.cpp kis_command.cpp kis_convolution_painter.cpp kis_fill_painter.cpp \ + kis_filter.cpp kis_filter_registry.cpp kis_filter_strategy.cpp \ + kis_filter_configuration.cpp kis_filter_config_widget.cpp kis_gradient.cpp kis_gradient_painter.cpp \ + kis_histogram.cpp kis_image.cpp kis_imagepipe_brush.cpp kis_iterator.cpp \ + kis_iterators_pixel.cpp kis_layer.cpp kis_group_layer.cpp kis_paint_layer.cpp kis_meta_registry.cpp \ + kis_nameserver.cpp kis_painter.cpp kis_paintop.cpp kis_paintop_registry.cpp kis_palette.cpp \ + kis_pattern.cpp kis_rect.cpp kis_resource.cpp kis_rotate_visitor.cpp \ + kis_selected_transaction.cpp kis_selection.cpp kis_strategy_move.cpp kis_transaction.cpp \ + kis_transform_worker.cpp kis_vec.cpp kis_paint_device.cpp kis_paint_device_iface.cpp \ + kis_paint_device_iface.skel kis_image_iface.cpp kis_image_iface.skel kis_basic_math_toolbox.cpp \ + kis_math_toolbox.cpp kis_exif_info.cpp kis_thread_pool.cpp kis_exif_value.cpp \ kis_filter_strategy.h kis_random_accessor.cpp kis_random_sub_accessor.cpp \ - kis_perspective_grid.cpp kis_perspectivetransform_worker.cpp kis_perspective_math.cpp kis_scale_visitor.cc + kis_perspective_grid.cpp kis_perspectivetransform_worker.cpp kis_perspective_math.cpp kis_scale_visitor.cpp noinst_HEADERS = kis_rotate_visitor.h kis_selected_transaction.h \ kis_strategy_move.h kis_transform_worker.h kis_datamanager.h kis_iteratorpixeltrait.h \ diff --git a/chalk/core/createdcop.py b/chalk/core/createdcop.py index ef7acc5e..337d9076 100755 --- a/chalk/core/createdcop.py +++ b/chalk/core/createdcop.py @@ -149,7 +149,7 @@ def createDCOP(header): classname = classname + part.capitalize() classname_upper = classname_upper + part.upper() + "_" ifaceheader = header[:-2] + "_iface.h" - ifaceimplementation = header[:-2] + "_iface.cc" + ifaceimplementation = header[:-2] + "_iface.cpp" ifaceclass = classname + "Iface" #print "with: ", implementation, classname, classname_upper, ifaceheader, ifaceimplementation, ifaceclass diff --git a/chalk/core/kis_adjustment_layer.cc b/chalk/core/kis_adjustment_layer.cc deleted file mode 100644 index 69e8b939..00000000 --- a/chalk/core/kis_adjustment_layer.cc +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * Copyright (c) 2005 Casper Boemann - * - * this program is free software; you can redistribute it and/or modify - * it under the terms of the gnu general public license as published by - * the free software foundation; either version 2 of the license, or - * (at your option) any later version. - * - * this program is distributed in the hope that it will be useful, - * but without any warranty; without even the implied warranty of - * merchantability or fitness for a particular purpose. see the - * gnu general public license for more details. - * - * you should have received a copy of the gnu general public license - * along with this program; if not, write to the free software - * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. - */ - -#include -#include - -#include "kis_debug_areas.h" -#include "kis_group_layer.h" -#include "kis_image.h" -#include "kis_layer.h" -#include "kis_adjustment_layer.h" -#include "kis_painter.h" -#include "kis_undo_adapter.h" -#include "kis_selection.h" -#include "kis_fill_painter.h" - -KisAdjustmentLayer::KisAdjustmentLayer(KisImageSP img, const TQString &name, KisFilterConfiguration * kfc, KisSelectionSP selection) - : KisLayer (img, name, OPACITY_OPAQUE) -{ - m_filterConfig = kfc; - setSelection( selection ); - m_cachedPaintDev = new KisPaintDevice( img->colorSpace(), name.latin1()); - m_showSelection = true; - Q_ASSERT(m_cachedPaintDev); - connect(img, TQT_SIGNAL(sigSelectionChanged(KisImageSP)), - this, TQT_SLOT(slotSelectionChanged(KisImageSP))); -} - -KisAdjustmentLayer::KisAdjustmentLayer(const KisAdjustmentLayer& rhs) - : KisLayer(rhs), KisLayerSupportsIndirectPainting(rhs) -{ - m_filterConfig = new KisFilterConfiguration(*rhs.m_filterConfig); - if (rhs.m_selection) { - m_selection = new KisSelection( *rhs.m_selection.data() ); - m_selection->setParentLayer(this); - m_selection->setInterestedInDirtyness(true); - connect(rhs.image(), TQT_SIGNAL(sigSelectionChanged(KisImageSP)), - this, TQT_SLOT(slotSelectionChanged(KisImageSP))); - } - m_cachedPaintDev = new KisPaintDevice( *rhs.m_cachedPaintDev.data() ); - m_showSelection = false; -} - - -KisAdjustmentLayer::~KisAdjustmentLayer() -{ - delete m_filterConfig; -} - - -KisLayerSP KisAdjustmentLayer::clone() const -{ - return new KisAdjustmentLayer(*this); -} - - -void KisAdjustmentLayer::resetCache() -{ - m_cachedPaintDev = new KisPaintDevice(image()->colorSpace(), name().latin1()); -} - -KisFilterConfiguration * KisAdjustmentLayer::filter() -{ - Q_ASSERT(m_filterConfig); - return m_filterConfig; -} - - -void KisAdjustmentLayer::setFilter(KisFilterConfiguration * filterConfig) -{ - Q_ASSERT(filterConfig); - m_filterConfig = filterConfig; -} - - -KisSelectionSP KisAdjustmentLayer::selection() -{ - return m_selection; -} - -void KisAdjustmentLayer::setSelection(KisSelectionSP selection) -{ - m_selection = new KisSelection(); - KisFillPainter gc(m_selection.data()); - KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); - - if (selection) { - gc.bitBlt(0, 0, COMPOSITE_COPY, selection.data(), - 0, 0, image()->bounds().width(), image()->bounds().height()); - } else { - gc.fillRect(image()->bounds(), KisColor(TQt::white, cs), MAX_SELECTED); - } - - gc.end(); - - m_selection->setParentLayer(this); - m_selection->setInterestedInDirtyness(true); -} - -void KisAdjustmentLayer::clearSelection() -{ - KisFillPainter gc(m_selection.data()); - KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); - - TQRect bounds = extent(); - bounds |= image()->bounds(); - gc.fillRect(bounds, KisColor(TQt::white, cs), MIN_SELECTED); - gc.end(); -} - - -TQ_INT32 KisAdjustmentLayer::x() const -{ - if (m_selection) - return m_selection->getX(); - else - return 0; -} - -void KisAdjustmentLayer::setX(TQ_INT32 x) -{ - if (m_selection) { - m_selection->setX(x); - resetCache(); - } - -} - -TQ_INT32 KisAdjustmentLayer::y() const -{ - if (m_selection) - return m_selection->getY(); - else - return 0; -} - -void KisAdjustmentLayer::setY(TQ_INT32 y) -{ - if (m_selection) { - m_selection->setY(y); - resetCache(); - } -} - -TQRect KisAdjustmentLayer::extent() const -{ - if (m_selection) - return m_selection->selectedRect(); - else if (image()) - return image()->bounds(); - else - return TQRect(); -} - -TQRect KisAdjustmentLayer::exactBounds() const -{ - if (m_selection) - return m_selection->selectedRect(); - else if (image()) - return image()->bounds(); - else - return TQRect(); -} - -bool KisAdjustmentLayer::accept(KisLayerVisitor & v) -{ - return v.visit( this ); -} - -void KisAdjustmentLayer::paintSelection(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - if (showSelection() && selection()) - selection()->paintSelection(img, x, y, w, h); -} - -void KisAdjustmentLayer::paintSelection(TQImage &img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize) -{ - if (showSelection() && selection()) - selection()->paintSelection(img, scaledImageRect, scaledImageSize, imageSize); -} - -TQImage KisAdjustmentLayer::createThumbnail(TQ_INT32 w, TQ_INT32 h) -{ - if (!selection()) - return TQImage(); - - int srcw, srch; - if( image() ) - { - srcw = image()->width(); - srch = image()->height(); - } - else - { - const TQRect e = extent(); - srcw = e.width(); - srch = e.height(); - } - - if (w > srcw) - { - w = srcw; - h = TQ_INT32(double(srcw) / w * h); - } - if (h > srch) - { - h = srch; - w = TQ_INT32(double(srch) / h * w); - } - - if (srcw > srch) - h = TQ_INT32(double(srch) / srcw * w); - else if (srch > srcw) - w = TQ_INT32(double(srcw) / srch * h); - - TQColor c; - TQ_UINT8 opacity; - TQImage img(w,h,32); - - for (TQ_INT32 y=0; y < h; ++y) { - TQ_INT32 iY = (y * srch ) / h; - for (TQ_INT32 x=0; x < w; ++x) { - TQ_INT32 iX = (x * srcw ) / w; - m_selection->pixel(iX, iY, &c, &opacity); - img.setPixel(x, y, tqRgb(opacity, opacity, opacity)); - } - } - - return img; -} - -void KisAdjustmentLayer::slotSelectionChanged(KisImageSP image) { - image->setModified(); -} - -#include "kis_adjustment_layer.moc" diff --git a/chalk/core/kis_adjustment_layer.cpp b/chalk/core/kis_adjustment_layer.cpp new file mode 100644 index 00000000..69e8b939 --- /dev/null +++ b/chalk/core/kis_adjustment_layer.cpp @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2002 Patrick Julien + * Copyright (c) 2005 Casper Boemann + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. + */ + +#include +#include + +#include "kis_debug_areas.h" +#include "kis_group_layer.h" +#include "kis_image.h" +#include "kis_layer.h" +#include "kis_adjustment_layer.h" +#include "kis_painter.h" +#include "kis_undo_adapter.h" +#include "kis_selection.h" +#include "kis_fill_painter.h" + +KisAdjustmentLayer::KisAdjustmentLayer(KisImageSP img, const TQString &name, KisFilterConfiguration * kfc, KisSelectionSP selection) + : KisLayer (img, name, OPACITY_OPAQUE) +{ + m_filterConfig = kfc; + setSelection( selection ); + m_cachedPaintDev = new KisPaintDevice( img->colorSpace(), name.latin1()); + m_showSelection = true; + Q_ASSERT(m_cachedPaintDev); + connect(img, TQT_SIGNAL(sigSelectionChanged(KisImageSP)), + this, TQT_SLOT(slotSelectionChanged(KisImageSP))); +} + +KisAdjustmentLayer::KisAdjustmentLayer(const KisAdjustmentLayer& rhs) + : KisLayer(rhs), KisLayerSupportsIndirectPainting(rhs) +{ + m_filterConfig = new KisFilterConfiguration(*rhs.m_filterConfig); + if (rhs.m_selection) { + m_selection = new KisSelection( *rhs.m_selection.data() ); + m_selection->setParentLayer(this); + m_selection->setInterestedInDirtyness(true); + connect(rhs.image(), TQT_SIGNAL(sigSelectionChanged(KisImageSP)), + this, TQT_SLOT(slotSelectionChanged(KisImageSP))); + } + m_cachedPaintDev = new KisPaintDevice( *rhs.m_cachedPaintDev.data() ); + m_showSelection = false; +} + + +KisAdjustmentLayer::~KisAdjustmentLayer() +{ + delete m_filterConfig; +} + + +KisLayerSP KisAdjustmentLayer::clone() const +{ + return new KisAdjustmentLayer(*this); +} + + +void KisAdjustmentLayer::resetCache() +{ + m_cachedPaintDev = new KisPaintDevice(image()->colorSpace(), name().latin1()); +} + +KisFilterConfiguration * KisAdjustmentLayer::filter() +{ + Q_ASSERT(m_filterConfig); + return m_filterConfig; +} + + +void KisAdjustmentLayer::setFilter(KisFilterConfiguration * filterConfig) +{ + Q_ASSERT(filterConfig); + m_filterConfig = filterConfig; +} + + +KisSelectionSP KisAdjustmentLayer::selection() +{ + return m_selection; +} + +void KisAdjustmentLayer::setSelection(KisSelectionSP selection) +{ + m_selection = new KisSelection(); + KisFillPainter gc(m_selection.data()); + KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); + + if (selection) { + gc.bitBlt(0, 0, COMPOSITE_COPY, selection.data(), + 0, 0, image()->bounds().width(), image()->bounds().height()); + } else { + gc.fillRect(image()->bounds(), KisColor(TQt::white, cs), MAX_SELECTED); + } + + gc.end(); + + m_selection->setParentLayer(this); + m_selection->setInterestedInDirtyness(true); +} + +void KisAdjustmentLayer::clearSelection() +{ + KisFillPainter gc(m_selection.data()); + KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); + + TQRect bounds = extent(); + bounds |= image()->bounds(); + gc.fillRect(bounds, KisColor(TQt::white, cs), MIN_SELECTED); + gc.end(); +} + + +TQ_INT32 KisAdjustmentLayer::x() const +{ + if (m_selection) + return m_selection->getX(); + else + return 0; +} + +void KisAdjustmentLayer::setX(TQ_INT32 x) +{ + if (m_selection) { + m_selection->setX(x); + resetCache(); + } + +} + +TQ_INT32 KisAdjustmentLayer::y() const +{ + if (m_selection) + return m_selection->getY(); + else + return 0; +} + +void KisAdjustmentLayer::setY(TQ_INT32 y) +{ + if (m_selection) { + m_selection->setY(y); + resetCache(); + } +} + +TQRect KisAdjustmentLayer::extent() const +{ + if (m_selection) + return m_selection->selectedRect(); + else if (image()) + return image()->bounds(); + else + return TQRect(); +} + +TQRect KisAdjustmentLayer::exactBounds() const +{ + if (m_selection) + return m_selection->selectedRect(); + else if (image()) + return image()->bounds(); + else + return TQRect(); +} + +bool KisAdjustmentLayer::accept(KisLayerVisitor & v) +{ + return v.visit( this ); +} + +void KisAdjustmentLayer::paintSelection(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + if (showSelection() && selection()) + selection()->paintSelection(img, x, y, w, h); +} + +void KisAdjustmentLayer::paintSelection(TQImage &img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize) +{ + if (showSelection() && selection()) + selection()->paintSelection(img, scaledImageRect, scaledImageSize, imageSize); +} + +TQImage KisAdjustmentLayer::createThumbnail(TQ_INT32 w, TQ_INT32 h) +{ + if (!selection()) + return TQImage(); + + int srcw, srch; + if( image() ) + { + srcw = image()->width(); + srch = image()->height(); + } + else + { + const TQRect e = extent(); + srcw = e.width(); + srch = e.height(); + } + + if (w > srcw) + { + w = srcw; + h = TQ_INT32(double(srcw) / w * h); + } + if (h > srch) + { + h = srch; + w = TQ_INT32(double(srch) / h * w); + } + + if (srcw > srch) + h = TQ_INT32(double(srch) / srcw * w); + else if (srch > srcw) + w = TQ_INT32(double(srcw) / srch * h); + + TQColor c; + TQ_UINT8 opacity; + TQImage img(w,h,32); + + for (TQ_INT32 y=0; y < h; ++y) { + TQ_INT32 iY = (y * srch ) / h; + for (TQ_INT32 x=0; x < w; ++x) { + TQ_INT32 iX = (x * srcw ) / w; + m_selection->pixel(iX, iY, &c, &opacity); + img.setPixel(x, y, tqRgb(opacity, opacity, opacity)); + } + } + + return img; +} + +void KisAdjustmentLayer::slotSelectionChanged(KisImageSP image) { + image->setModified(); +} + +#include "kis_adjustment_layer.moc" diff --git a/chalk/core/kis_alpha_mask.cc b/chalk/core/kis_alpha_mask.cc deleted file mode 100644 index 15296308..00000000 --- a/chalk/core/kis_alpha_mask.cc +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2004 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include - -#include - -#include "kis_global.h" -#include "kis_alpha_mask.h" - -KisAlphaMask::KisAlphaMask(const TQImage& img, bool hasColor) -{ - m_width = img.width(); - m_height = img.height(); - - if (hasColor) { - copyAlpha(img); - } - else { - computeAlpha(img); - } -} - -KisAlphaMask::KisAlphaMask(const TQImage& img) -{ - m_width = img.width(); - m_height = img.height(); - - if (!img.allGray()) { - copyAlpha(img); - } - else { - computeAlpha(img); - } -} - -KisAlphaMask::KisAlphaMask(TQ_INT32 width, TQ_INT32 height) -{ - m_width = width; - m_height = height; - - m_data.resize(width * height, OPACITY_TRANSPARENT); -} - -KisAlphaMask::~KisAlphaMask() -{ -} - -TQ_INT32 KisAlphaMask::width() const -{ - return m_width; -} - -TQ_INT32 KisAlphaMask::height() const -{ - return m_height; -} - -void KisAlphaMask::setAlphaAt(TQ_INT32 x, TQ_INT32 y, TQ_UINT8 alpha) -{ - if (y >= 0 && y < m_height && x >= 0 && x < m_width) { - m_data[(y * m_width) + x] = alpha; - } -} - -void KisAlphaMask::copyAlpha(const TQImage& img) -{ - for (int y = 0; y < img.height(); y++) { - for (int x = 0; x < img.width(); x++) { - TQRgb c = img.pixel(x,y); - TQ_UINT8 a = (tqGray(c) * tqAlpha(c)) / 255; - m_data.push_back(a); - - } - } -} - -void KisAlphaMask::computeAlpha(const TQImage& img) -{ - // The brushes are mostly grayscale on a white background, - // although some do have a colors. The alpha channel is seldom - // used, so we take the average gray value of this pixel of - // the brush as the setting for the opacitiy. We need to - // invert it, because 255, 255, 255 is white, which is - // completely transparent, but 255 corresponds to - // OPACITY_OPAQUE. - - for (int y = 0; y < img.height(); y++) { - for (int x = 0; x < img.width(); x++) { - m_data.push_back(255 - tqRed(img.pixel(x, y))); - } - } -} - -KisAlphaMaskSP KisAlphaMask::interpolate(KisAlphaMaskSP mask1, KisAlphaMaskSP mask2, double t) -{ - Q_ASSERT((mask1->width() == mask2->width()) && (mask1->height() == mask2->height())); - Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); - - int width = mask1->width(); - int height = mask1->height(); - KisAlphaMaskSP outputMask = new KisAlphaMask(width, height); - TQ_CHECK_PTR(outputMask); - - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - TQ_UINT8 d = static_cast((1 - t) * mask1->alphaAt(x, y) + t * mask2->alphaAt(x, y)); - outputMask->setAlphaAt(x, y, d); - } - } - - return outputMask; -} - - diff --git a/chalk/core/kis_alpha_mask.cpp b/chalk/core/kis_alpha_mask.cpp new file mode 100644 index 00000000..15296308 --- /dev/null +++ b/chalk/core/kis_alpha_mask.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include + +#include "kis_global.h" +#include "kis_alpha_mask.h" + +KisAlphaMask::KisAlphaMask(const TQImage& img, bool hasColor) +{ + m_width = img.width(); + m_height = img.height(); + + if (hasColor) { + copyAlpha(img); + } + else { + computeAlpha(img); + } +} + +KisAlphaMask::KisAlphaMask(const TQImage& img) +{ + m_width = img.width(); + m_height = img.height(); + + if (!img.allGray()) { + copyAlpha(img); + } + else { + computeAlpha(img); + } +} + +KisAlphaMask::KisAlphaMask(TQ_INT32 width, TQ_INT32 height) +{ + m_width = width; + m_height = height; + + m_data.resize(width * height, OPACITY_TRANSPARENT); +} + +KisAlphaMask::~KisAlphaMask() +{ +} + +TQ_INT32 KisAlphaMask::width() const +{ + return m_width; +} + +TQ_INT32 KisAlphaMask::height() const +{ + return m_height; +} + +void KisAlphaMask::setAlphaAt(TQ_INT32 x, TQ_INT32 y, TQ_UINT8 alpha) +{ + if (y >= 0 && y < m_height && x >= 0 && x < m_width) { + m_data[(y * m_width) + x] = alpha; + } +} + +void KisAlphaMask::copyAlpha(const TQImage& img) +{ + for (int y = 0; y < img.height(); y++) { + for (int x = 0; x < img.width(); x++) { + TQRgb c = img.pixel(x,y); + TQ_UINT8 a = (tqGray(c) * tqAlpha(c)) / 255; + m_data.push_back(a); + + } + } +} + +void KisAlphaMask::computeAlpha(const TQImage& img) +{ + // The brushes are mostly grayscale on a white background, + // although some do have a colors. The alpha channel is seldom + // used, so we take the average gray value of this pixel of + // the brush as the setting for the opacitiy. We need to + // invert it, because 255, 255, 255 is white, which is + // completely transparent, but 255 corresponds to + // OPACITY_OPAQUE. + + for (int y = 0; y < img.height(); y++) { + for (int x = 0; x < img.width(); x++) { + m_data.push_back(255 - tqRed(img.pixel(x, y))); + } + } +} + +KisAlphaMaskSP KisAlphaMask::interpolate(KisAlphaMaskSP mask1, KisAlphaMaskSP mask2, double t) +{ + Q_ASSERT((mask1->width() == mask2->width()) && (mask1->height() == mask2->height())); + Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); + + int width = mask1->width(); + int height = mask1->height(); + KisAlphaMaskSP outputMask = new KisAlphaMask(width, height); + TQ_CHECK_PTR(outputMask); + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + TQ_UINT8 d = static_cast((1 - t) * mask1->alphaAt(x, y) + t * mask2->alphaAt(x, y)); + outputMask->setAlphaAt(x, y, d); + } + } + + return outputMask; +} + + diff --git a/chalk/core/kis_autobrush_resource.cc b/chalk/core/kis_autobrush_resource.cc deleted file mode 100644 index 9a09fe78..00000000 --- a/chalk/core/kis_autobrush_resource.cc +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2004 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "kis_autobrush_resource.h" -#include - -void KisAutobrushShape::createBrush( TQImage* img) -{ - img->create(m_w, m_h, 32); - for(int j = 0; j < m_h; j++) - { - for(int i = 0; i < m_w; i++) - { - TQ_INT8 v = valueAt(i,j); - img->setPixel( i, j, tqRgb(v,v,v)); - } - } -} - -KisAutobrushCircleShape::KisAutobrushCircleShape(TQ_INT32 w, TQ_INT32 h, double fh, double fv) - : KisAutobrushShape( w, h, w / 2.0 - fh, h / 2.0 - fv), - m_xcentre ( w / 2.0 ), - m_ycentre ( h / 2.0 ), - m_xcoef ( 2.0 / w ), - m_ycoef ( 2.0 / h ), - m_xfadecoef ( (m_fh == 0) ? 1 : ( 1.0 / m_fh)), - m_yfadecoef ( (m_fv == 0) ? 1 : ( 1.0 / m_fv)) -{ -} -TQ_INT8 KisAutobrushCircleShape::valueAt(TQ_INT32 x, TQ_INT32 y) -{ - double xr = (x - m_xcentre) + 0.5; - double yr = (y - m_ycentre) + 0.5; - double n = norme( xr * m_xcoef, yr * m_ycoef); - if( n > 1 ) - { - return 255; - } - else - { - double normeFade = norme( xr * m_xfadecoef, yr * m_yfadecoef ); - if( normeFade > 1) - { - double xle, yle; - // xle stands for x-coordinate limit exterior - // yle stands for y-coordinate limit exterior - // we are computing the coordinate on the external ellipse in order to compute - // the fade value - if( xr == 0 ) - { - xle = 0; - yle = yr > 0 ? 1/m_ycoef : -1/m_ycoef; - } else { - double c = yr / (double)xr; - xle = sqrt(1 / norme( m_xcoef, c * m_ycoef )); - xle = xr > 0 ? xle : -xle; - yle = xle * c; - } - // On the internal limit of the fade area, normeFade is equal to 1 - double normeFadeLimitE = norme( xle * m_xfadecoef, yle * m_yfadecoef ); - return (uchar)(255 * ( normeFade - 1 ) / ( normeFadeLimitE - 1 )); - } else { - return 0; - } - } -} - -KisAutobrushRectShape::KisAutobrushRectShape(TQ_INT32 w, TQ_INT32 h, double fh, double fv) - : KisAutobrushShape( w, h, w / 2.0 - fh, h / 2.0 - fv), - m_xcentre ( w / 2.0 ), - m_ycentre ( h / 2.0 ), - m_c( fv/fh) -{ -} -TQ_INT8 KisAutobrushRectShape::valueAt(TQ_INT32 x, TQ_INT32 y) -{ - double xr = TQABS(x - m_xcentre); - double yr = TQABS(y - m_ycentre); - if( xr > m_fh || yr > m_fv ) - { - if( yr <= ((xr - m_fh) * m_c + m_fv ) ) - { - return (uchar)(255 * (xr - m_fh) / (m_w - m_fh)); - } else { - return (uchar)(255 * (yr - m_fv) / (m_w - m_fv)); - } - } - else { - return 0; - } -} diff --git a/chalk/core/kis_autobrush_resource.cpp b/chalk/core/kis_autobrush_resource.cpp new file mode 100644 index 00000000..9a09fe78 --- /dev/null +++ b/chalk/core/kis_autobrush_resource.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2004 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_autobrush_resource.h" +#include + +void KisAutobrushShape::createBrush( TQImage* img) +{ + img->create(m_w, m_h, 32); + for(int j = 0; j < m_h; j++) + { + for(int i = 0; i < m_w; i++) + { + TQ_INT8 v = valueAt(i,j); + img->setPixel( i, j, tqRgb(v,v,v)); + } + } +} + +KisAutobrushCircleShape::KisAutobrushCircleShape(TQ_INT32 w, TQ_INT32 h, double fh, double fv) + : KisAutobrushShape( w, h, w / 2.0 - fh, h / 2.0 - fv), + m_xcentre ( w / 2.0 ), + m_ycentre ( h / 2.0 ), + m_xcoef ( 2.0 / w ), + m_ycoef ( 2.0 / h ), + m_xfadecoef ( (m_fh == 0) ? 1 : ( 1.0 / m_fh)), + m_yfadecoef ( (m_fv == 0) ? 1 : ( 1.0 / m_fv)) +{ +} +TQ_INT8 KisAutobrushCircleShape::valueAt(TQ_INT32 x, TQ_INT32 y) +{ + double xr = (x - m_xcentre) + 0.5; + double yr = (y - m_ycentre) + 0.5; + double n = norme( xr * m_xcoef, yr * m_ycoef); + if( n > 1 ) + { + return 255; + } + else + { + double normeFade = norme( xr * m_xfadecoef, yr * m_yfadecoef ); + if( normeFade > 1) + { + double xle, yle; + // xle stands for x-coordinate limit exterior + // yle stands for y-coordinate limit exterior + // we are computing the coordinate on the external ellipse in order to compute + // the fade value + if( xr == 0 ) + { + xle = 0; + yle = yr > 0 ? 1/m_ycoef : -1/m_ycoef; + } else { + double c = yr / (double)xr; + xle = sqrt(1 / norme( m_xcoef, c * m_ycoef )); + xle = xr > 0 ? xle : -xle; + yle = xle * c; + } + // On the internal limit of the fade area, normeFade is equal to 1 + double normeFadeLimitE = norme( xle * m_xfadecoef, yle * m_yfadecoef ); + return (uchar)(255 * ( normeFade - 1 ) / ( normeFadeLimitE - 1 )); + } else { + return 0; + } + } +} + +KisAutobrushRectShape::KisAutobrushRectShape(TQ_INT32 w, TQ_INT32 h, double fh, double fv) + : KisAutobrushShape( w, h, w / 2.0 - fh, h / 2.0 - fv), + m_xcentre ( w / 2.0 ), + m_ycentre ( h / 2.0 ), + m_c( fv/fh) +{ +} +TQ_INT8 KisAutobrushRectShape::valueAt(TQ_INT32 x, TQ_INT32 y) +{ + double xr = TQABS(x - m_xcentre); + double yr = TQABS(y - m_ycentre); + if( xr > m_fh || yr > m_fv ) + { + if( yr <= ((xr - m_fh) * m_c + m_fv ) ) + { + return (uchar)(255 * (xr - m_fh) / (m_w - m_fh)); + } else { + return (uchar)(255 * (yr - m_fv) / (m_w - m_fv)); + } + } + else { + return 0; + } +} diff --git a/chalk/core/kis_autogradient_resource.cc b/chalk/core/kis_autogradient_resource.cc deleted file mode 100644 index 671e8fc2..00000000 --- a/chalk/core/kis_autogradient_resource.cc +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (c) 2004 Cyrille Berger - * 2004 Sven Langkamp - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "kis_gradient.h" -#include "kis_autogradient_resource.h" - -// FIXME: use the same #define as in kis_gradient.cc, probably best customizable? -#define PREVIEW_WIDTH 64 -#define PREVIEW_HEIGHT 64 - - -void KisAutogradientResource::createSegment( int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, TQColor left, TQColor right ) -{ - pushSegment(new KisGradientSegment(interpolation, colorInterpolation, startOffset, middleOffset, endOffset, Color( left, 1 ), Color( right, 1 ))); - -} - -const TQValueVector KisAutogradientResource::getHandlePositions() const -{ - TQValueVector handlePositions; - - handlePositions.push_back(m_segments[0]->startOffset()); - for (uint i = 0; i < m_segments.count(); i++) - { - handlePositions.push_back(m_segments[i]->endOffset()); - } - return handlePositions; -} - -const TQValueVector KisAutogradientResource::getMiddleHandlePositions() const -{ - TQValueVector middleHandlePositions; - - for (uint i = 0; i < m_segments.count(); i++) - { - middleHandlePositions.push_back(m_segments[i]->middleOffset()); - } - return middleHandlePositions; -} - -void KisAutogradientResource::moveSegmentStartOffset( KisGradientSegment* segment, double t) -{ - TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); - if ( it != m_segments.end() ) - { - if ( it == m_segments.begin() ) - { - segment->setStartOffset( 0.0 ); - return; - } - KisGradientSegment* previousSegment = (*(it-1)); - if ( t > segment->startOffset() ) - { - if( t > segment->middleOffset() ) - t = segment->middleOffset(); - } - else { - if( t < previousSegment->middleOffset() ) - t = previousSegment->middleOffset(); - } - previousSegment->setEndOffset( t ); - segment->setStartOffset( t ); - } -} - -void KisAutogradientResource::moveSegmentEndOffset( KisGradientSegment* segment, double t) -{ - TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); - if ( it != m_segments.end() ) - { - if ( it+1 == m_segments.end() ) - { - segment->setEndOffset( 1.0 ); - return; - } - KisGradientSegment* followingSegment = (*(it+1)); - if ( t < segment->endOffset() ) - { - if( t < segment->middleOffset() ) - t = segment->middleOffset(); - } - else { - if( t > followingSegment->middleOffset() ) - t = followingSegment->middleOffset(); - } - followingSegment->setStartOffset( t ); - segment->setEndOffset( t ); - } -} - -void KisAutogradientResource::moveSegmentMiddleOffset( KisGradientSegment* segment, double t) -{ - if( segment ) - { - if( t > segment->endOffset() ) - segment->setMiddleOffset( segment->endOffset() ); - else if( t < segment->startOffset() ) - segment->setMiddleOffset( segment->startOffset() ); - else - segment->setMiddleOffset( t ); - } -} - -void KisAutogradientResource::splitSegment( KisGradientSegment* segment ) -{ - Q_ASSERT(segment != 0); - TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); - if ( it != m_segments.end() ) - { - KisGradientSegment* newSegment = new KisGradientSegment( - segment->interpolation(), segment->colorInterpolation(), - segment ->startOffset(), - ( segment->middleOffset() - segment->startOffset() ) / 2 + segment->startOffset(), - segment->middleOffset(), - segment->startColor(), - segment->colorAt( segment->middleOffset() ) ); - m_segments.insert( it, newSegment ); - segment->setStartColor( segment->colorAt( segment->middleOffset() ) ); - segment->setStartOffset( segment->middleOffset() ); - segment->setMiddleOffset( ( segment->endOffset() - segment->startOffset() ) / 2 + segment->startOffset() ); - } -} - -void KisAutogradientResource::duplicateSegment( KisGradientSegment* segment ) -{ - Q_ASSERT(segment != 0); - TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); - if ( it != m_segments.end() ) - { - double middlePostionPercentage = ( segment->middleOffset() - segment->startOffset() ) / segment->length(); - double center = segment->startOffset() + segment->length() / 2; - KisGradientSegment* newSegment = new KisGradientSegment( - segment->interpolation(), segment->colorInterpolation(), - segment ->startOffset(), - segment->length() / 2 * middlePostionPercentage + segment->startOffset(), - center, segment->startColor(), - segment->endColor() ); - m_segments.insert( it, newSegment ); - segment->setStartOffset( center ); - segment->setMiddleOffset( segment->length() * middlePostionPercentage + segment->startOffset() ); - } -} - -void KisAutogradientResource::mirrorSegment( KisGradientSegment* segment ) -{ - Q_ASSERT(segment != 0); - Color tmpColor = segment->startColor(); - segment->setStartColor( segment->endColor() ); - segment->setEndColor( tmpColor ); - segment->setMiddleOffset( segment->endOffset() - ( segment->middleOffset() - segment->startOffset() ) ); - - if( segment->interpolation() == INTERP_SPHERE_INCREASING ) - segment->setInterpolation( INTERP_SPHERE_DECREASING ); - else if( segment->interpolation() == INTERP_SPHERE_DECREASING ) - segment->setInterpolation( INTERP_SPHERE_INCREASING ); - - if( segment->colorInterpolation() == COLOR_INTERP_HSV_CW ) - segment->setColorInterpolation( COLOR_INTERP_HSV_CCW ); - else if( segment->colorInterpolation() == COLOR_INTERP_HSV_CCW ) - segment->setColorInterpolation( COLOR_INTERP_HSV_CW ); -} - -KisGradientSegment* KisAutogradientResource::removeSegment( KisGradientSegment* segment ) -{ - Q_ASSERT(segment != 0); - if( m_segments.count() < 2 ) - return 0; - TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); - if ( it != m_segments.end() ) - { - double middlePostionPercentage; - KisGradientSegment* nextSegment; - if( it == m_segments.begin() ) - { - nextSegment = (*(it+1)); - middlePostionPercentage = ( nextSegment->middleOffset() - nextSegment->startOffset() ) / nextSegment->length(); - nextSegment->setStartOffset( segment->startOffset() ); - nextSegment->setMiddleOffset( middlePostionPercentage * nextSegment->length() + nextSegment->startOffset() ); - } - else - { - nextSegment = (*(it-1)); - middlePostionPercentage = ( nextSegment->middleOffset() - nextSegment->startOffset() ) / nextSegment->length(); - nextSegment->setEndOffset( segment->endOffset() ); - nextSegment->setMiddleOffset( middlePostionPercentage * nextSegment->length() + nextSegment->startOffset() ); - } - - delete segment; - m_segments.erase( it ); - return nextSegment; - } - return 0; -} - -bool KisAutogradientResource::removeSegmentPossible() const -{ - if( m_segments.count() < 2 ) - return false; - return true; -} - -void KisAutogradientResource::updatePreview() -{ - setImage( generatePreview( PREVIEW_WIDTH, PREVIEW_HEIGHT ) ); -} diff --git a/chalk/core/kis_autogradient_resource.cpp b/chalk/core/kis_autogradient_resource.cpp new file mode 100644 index 00000000..aaac54ef --- /dev/null +++ b/chalk/core/kis_autogradient_resource.cpp @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2004 Cyrille Berger + * 2004 Sven Langkamp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_gradient.h" +#include "kis_autogradient_resource.h" + +// FIXME: use the same #define as in kis_gradient.cpp, probably best customizable? +#define PREVIEW_WIDTH 64 +#define PREVIEW_HEIGHT 64 + + +void KisAutogradientResource::createSegment( int interpolation, int colorInterpolation, double startOffset, double endOffset, double middleOffset, TQColor left, TQColor right ) +{ + pushSegment(new KisGradientSegment(interpolation, colorInterpolation, startOffset, middleOffset, endOffset, Color( left, 1 ), Color( right, 1 ))); + +} + +const TQValueVector KisAutogradientResource::getHandlePositions() const +{ + TQValueVector handlePositions; + + handlePositions.push_back(m_segments[0]->startOffset()); + for (uint i = 0; i < m_segments.count(); i++) + { + handlePositions.push_back(m_segments[i]->endOffset()); + } + return handlePositions; +} + +const TQValueVector KisAutogradientResource::getMiddleHandlePositions() const +{ + TQValueVector middleHandlePositions; + + for (uint i = 0; i < m_segments.count(); i++) + { + middleHandlePositions.push_back(m_segments[i]->middleOffset()); + } + return middleHandlePositions; +} + +void KisAutogradientResource::moveSegmentStartOffset( KisGradientSegment* segment, double t) +{ + TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); + if ( it != m_segments.end() ) + { + if ( it == m_segments.begin() ) + { + segment->setStartOffset( 0.0 ); + return; + } + KisGradientSegment* previousSegment = (*(it-1)); + if ( t > segment->startOffset() ) + { + if( t > segment->middleOffset() ) + t = segment->middleOffset(); + } + else { + if( t < previousSegment->middleOffset() ) + t = previousSegment->middleOffset(); + } + previousSegment->setEndOffset( t ); + segment->setStartOffset( t ); + } +} + +void KisAutogradientResource::moveSegmentEndOffset( KisGradientSegment* segment, double t) +{ + TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); + if ( it != m_segments.end() ) + { + if ( it+1 == m_segments.end() ) + { + segment->setEndOffset( 1.0 ); + return; + } + KisGradientSegment* followingSegment = (*(it+1)); + if ( t < segment->endOffset() ) + { + if( t < segment->middleOffset() ) + t = segment->middleOffset(); + } + else { + if( t > followingSegment->middleOffset() ) + t = followingSegment->middleOffset(); + } + followingSegment->setStartOffset( t ); + segment->setEndOffset( t ); + } +} + +void KisAutogradientResource::moveSegmentMiddleOffset( KisGradientSegment* segment, double t) +{ + if( segment ) + { + if( t > segment->endOffset() ) + segment->setMiddleOffset( segment->endOffset() ); + else if( t < segment->startOffset() ) + segment->setMiddleOffset( segment->startOffset() ); + else + segment->setMiddleOffset( t ); + } +} + +void KisAutogradientResource::splitSegment( KisGradientSegment* segment ) +{ + Q_ASSERT(segment != 0); + TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); + if ( it != m_segments.end() ) + { + KisGradientSegment* newSegment = new KisGradientSegment( + segment->interpolation(), segment->colorInterpolation(), + segment ->startOffset(), + ( segment->middleOffset() - segment->startOffset() ) / 2 + segment->startOffset(), + segment->middleOffset(), + segment->startColor(), + segment->colorAt( segment->middleOffset() ) ); + m_segments.insert( it, newSegment ); + segment->setStartColor( segment->colorAt( segment->middleOffset() ) ); + segment->setStartOffset( segment->middleOffset() ); + segment->setMiddleOffset( ( segment->endOffset() - segment->startOffset() ) / 2 + segment->startOffset() ); + } +} + +void KisAutogradientResource::duplicateSegment( KisGradientSegment* segment ) +{ + Q_ASSERT(segment != 0); + TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); + if ( it != m_segments.end() ) + { + double middlePostionPercentage = ( segment->middleOffset() - segment->startOffset() ) / segment->length(); + double center = segment->startOffset() + segment->length() / 2; + KisGradientSegment* newSegment = new KisGradientSegment( + segment->interpolation(), segment->colorInterpolation(), + segment ->startOffset(), + segment->length() / 2 * middlePostionPercentage + segment->startOffset(), + center, segment->startColor(), + segment->endColor() ); + m_segments.insert( it, newSegment ); + segment->setStartOffset( center ); + segment->setMiddleOffset( segment->length() * middlePostionPercentage + segment->startOffset() ); + } +} + +void KisAutogradientResource::mirrorSegment( KisGradientSegment* segment ) +{ + Q_ASSERT(segment != 0); + Color tmpColor = segment->startColor(); + segment->setStartColor( segment->endColor() ); + segment->setEndColor( tmpColor ); + segment->setMiddleOffset( segment->endOffset() - ( segment->middleOffset() - segment->startOffset() ) ); + + if( segment->interpolation() == INTERP_SPHERE_INCREASING ) + segment->setInterpolation( INTERP_SPHERE_DECREASING ); + else if( segment->interpolation() == INTERP_SPHERE_DECREASING ) + segment->setInterpolation( INTERP_SPHERE_INCREASING ); + + if( segment->colorInterpolation() == COLOR_INTERP_HSV_CW ) + segment->setColorInterpolation( COLOR_INTERP_HSV_CCW ); + else if( segment->colorInterpolation() == COLOR_INTERP_HSV_CCW ) + segment->setColorInterpolation( COLOR_INTERP_HSV_CW ); +} + +KisGradientSegment* KisAutogradientResource::removeSegment( KisGradientSegment* segment ) +{ + Q_ASSERT(segment != 0); + if( m_segments.count() < 2 ) + return 0; + TQValueVector::iterator it = tqFind( m_segments.begin(), m_segments.end(), segment ); + if ( it != m_segments.end() ) + { + double middlePostionPercentage; + KisGradientSegment* nextSegment; + if( it == m_segments.begin() ) + { + nextSegment = (*(it+1)); + middlePostionPercentage = ( nextSegment->middleOffset() - nextSegment->startOffset() ) / nextSegment->length(); + nextSegment->setStartOffset( segment->startOffset() ); + nextSegment->setMiddleOffset( middlePostionPercentage * nextSegment->length() + nextSegment->startOffset() ); + } + else + { + nextSegment = (*(it-1)); + middlePostionPercentage = ( nextSegment->middleOffset() - nextSegment->startOffset() ) / nextSegment->length(); + nextSegment->setEndOffset( segment->endOffset() ); + nextSegment->setMiddleOffset( middlePostionPercentage * nextSegment->length() + nextSegment->startOffset() ); + } + + delete segment; + m_segments.erase( it ); + return nextSegment; + } + return 0; +} + +bool KisAutogradientResource::removeSegmentPossible() const +{ + if( m_segments.count() < 2 ) + return false; + return true; +} + +void KisAutogradientResource::updatePreview() +{ + setImage( generatePreview( PREVIEW_WIDTH, PREVIEW_HEIGHT ) ); +} diff --git a/chalk/core/kis_background.cc b/chalk/core/kis_background.cc deleted file mode 100644 index 506d49e8..00000000 --- a/chalk/core/kis_background.cc +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * - * this program is free software; you can redistribute it and/or modify - * it under the terms of the gnu general public license as published by - * the free software foundation; either version 2 of the license, or - * (at your option) any later version. - * - * this program is distributed in the hope that it will be useful, - * but without any warranty; without even the implied warranty of - * merchantability or fitness for a particular purpose. see the - * gnu general public license for more details. - * - * you should have received a copy of the gnu general public license - * along with this program; if not, write to the free software - * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. - */ -#include "kis_global.h" -#include "kis_background.h" -#include "kis_integer_maths.h" - -KisBackground::KisBackground() - : TDEShared() -{ - m_patternTile = TQImage(PATTERN_WIDTH, PATTERN_HEIGHT, 32); - m_patternTile.setAlphaBuffer(false); - - for (int y = 0; y < PATTERN_HEIGHT; y++) - { - for (int x = 0; x < PATTERN_WIDTH; x++) - { - TQ_UINT8 v = 128 + 63 * ((x / 16 + y / 16) % 2); - m_patternTile.setPixel(x, y, tqRgb(v, v, v)); - } - } -} - -KisBackground::~KisBackground() -{ -} - -const TQImage& KisBackground::patternTile() const -{ - return m_patternTile; -} - -void KisBackground::paintBackground(TQImage image, int imageLeftX, int imageTopY) -{ - int patternLeftX; - - if (imageLeftX >= 0) { - patternLeftX = imageLeftX % PATTERN_WIDTH; - } else { - patternLeftX = (PATTERN_WIDTH - (-imageLeftX % PATTERN_WIDTH)) % PATTERN_WIDTH; - } - - int patternTopY; - - if (imageTopY >= 0) { - patternTopY = imageTopY % PATTERN_HEIGHT; - } else { - patternTopY = (PATTERN_HEIGHT - (-imageTopY % PATTERN_HEIGHT)) % PATTERN_HEIGHT; - } - - int imageWidth = image.width(); - int imageHeight = image.height(); - - int patternY = patternTopY; - - for (int y = 0; y < imageHeight; y++) - { - TQRgb *imagePixelPtr = reinterpret_cast(image.scanLine(y)); - const TQRgb *patternScanLine = reinterpret_cast(m_patternTile.scanLine(patternY)); - int patternX = patternLeftX; - - for (int x = 0; x < imageWidth; x++) - { - TQRgb imagePixel = *imagePixelPtr; - TQ_UINT8 imagePixelAlpha = tqAlpha(imagePixel); - - if (imagePixelAlpha != 255) { - - TQRgb patternPixel = patternScanLine[patternX]; - TQ_UINT8 imageRed = UINT8_BLEND(tqRed(imagePixel), tqRed(patternPixel), imagePixelAlpha); - TQ_UINT8 imageGreen = UINT8_BLEND(tqGreen(imagePixel), tqGreen(patternPixel), imagePixelAlpha); - TQ_UINT8 imageBlue = UINT8_BLEND(tqBlue(imagePixel), tqBlue(patternPixel), imagePixelAlpha); - - *imagePixelPtr = tqRgba(imageRed, imageGreen, imageBlue, 255); - } - - ++imagePixelPtr; - ++patternX; - - if (patternX == PATTERN_WIDTH) { - patternX = 0; - } - } - - ++patternY; - - if (patternY == PATTERN_HEIGHT) { - patternY = 0; - } - } -} - -void KisBackground::paintBackground(TQImage img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize) -{ - if (scaledImageRect.isEmpty() || scaledImageSize.isEmpty() || imageSize.isEmpty()) { - return; - } - - Q_ASSERT(img.size() == scaledImageRect.size()); - - if (img.size() != scaledImageRect.size()) { - return; - } - - TQ_INT32 imageWidth = imageSize.width(); - TQ_INT32 imageHeight = imageSize.height(); - - for (TQ_INT32 y = 0; y < scaledImageRect.height(); ++y) { - - TQ_INT32 scaledY = scaledImageRect.y() + y; - TQ_INT32 srcY = (scaledY * imageHeight) / scaledImageSize.height(); - TQ_INT32 patternY = srcY % PATTERN_HEIGHT; - - TQRgb *imagePixelPtr = reinterpret_cast(img.scanLine(y)); - const TQRgb *patternScanLine = reinterpret_cast(m_patternTile.scanLine(patternY)); - - for (TQ_INT32 x = 0; x < scaledImageRect.width(); ++x) { - - TQRgb imagePixel = *imagePixelPtr; - TQ_UINT8 imagePixelAlpha = tqAlpha(imagePixel); - - if (imagePixelAlpha != 255) { - - TQ_INT32 scaledX = scaledImageRect.x() + x; - TQ_INT32 srcX = (scaledX * imageWidth) / scaledImageSize.width(); - TQ_INT32 patternX = srcX % PATTERN_WIDTH; - - TQRgb patternPixel = patternScanLine[patternX]; - TQ_UINT8 imageRed = UINT8_BLEND(tqRed(imagePixel), tqRed(patternPixel), imagePixelAlpha); - TQ_UINT8 imageGreen = UINT8_BLEND(tqGreen(imagePixel), tqGreen(patternPixel), imagePixelAlpha); - TQ_UINT8 imageBlue = UINT8_BLEND(tqBlue(imagePixel), tqBlue(patternPixel), imagePixelAlpha); - - *imagePixelPtr = tqRgba(imageRed, imageGreen, imageBlue, 255); - } - - ++imagePixelPtr; - } - } -} - - diff --git a/chalk/core/kis_background.cpp b/chalk/core/kis_background.cpp new file mode 100644 index 00000000..506d49e8 --- /dev/null +++ b/chalk/core/kis_background.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2002 Patrick Julien + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. + */ +#include "kis_global.h" +#include "kis_background.h" +#include "kis_integer_maths.h" + +KisBackground::KisBackground() + : TDEShared() +{ + m_patternTile = TQImage(PATTERN_WIDTH, PATTERN_HEIGHT, 32); + m_patternTile.setAlphaBuffer(false); + + for (int y = 0; y < PATTERN_HEIGHT; y++) + { + for (int x = 0; x < PATTERN_WIDTH; x++) + { + TQ_UINT8 v = 128 + 63 * ((x / 16 + y / 16) % 2); + m_patternTile.setPixel(x, y, tqRgb(v, v, v)); + } + } +} + +KisBackground::~KisBackground() +{ +} + +const TQImage& KisBackground::patternTile() const +{ + return m_patternTile; +} + +void KisBackground::paintBackground(TQImage image, int imageLeftX, int imageTopY) +{ + int patternLeftX; + + if (imageLeftX >= 0) { + patternLeftX = imageLeftX % PATTERN_WIDTH; + } else { + patternLeftX = (PATTERN_WIDTH - (-imageLeftX % PATTERN_WIDTH)) % PATTERN_WIDTH; + } + + int patternTopY; + + if (imageTopY >= 0) { + patternTopY = imageTopY % PATTERN_HEIGHT; + } else { + patternTopY = (PATTERN_HEIGHT - (-imageTopY % PATTERN_HEIGHT)) % PATTERN_HEIGHT; + } + + int imageWidth = image.width(); + int imageHeight = image.height(); + + int patternY = patternTopY; + + for (int y = 0; y < imageHeight; y++) + { + TQRgb *imagePixelPtr = reinterpret_cast(image.scanLine(y)); + const TQRgb *patternScanLine = reinterpret_cast(m_patternTile.scanLine(patternY)); + int patternX = patternLeftX; + + for (int x = 0; x < imageWidth; x++) + { + TQRgb imagePixel = *imagePixelPtr; + TQ_UINT8 imagePixelAlpha = tqAlpha(imagePixel); + + if (imagePixelAlpha != 255) { + + TQRgb patternPixel = patternScanLine[patternX]; + TQ_UINT8 imageRed = UINT8_BLEND(tqRed(imagePixel), tqRed(patternPixel), imagePixelAlpha); + TQ_UINT8 imageGreen = UINT8_BLEND(tqGreen(imagePixel), tqGreen(patternPixel), imagePixelAlpha); + TQ_UINT8 imageBlue = UINT8_BLEND(tqBlue(imagePixel), tqBlue(patternPixel), imagePixelAlpha); + + *imagePixelPtr = tqRgba(imageRed, imageGreen, imageBlue, 255); + } + + ++imagePixelPtr; + ++patternX; + + if (patternX == PATTERN_WIDTH) { + patternX = 0; + } + } + + ++patternY; + + if (patternY == PATTERN_HEIGHT) { + patternY = 0; + } + } +} + +void KisBackground::paintBackground(TQImage img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize) +{ + if (scaledImageRect.isEmpty() || scaledImageSize.isEmpty() || imageSize.isEmpty()) { + return; + } + + Q_ASSERT(img.size() == scaledImageRect.size()); + + if (img.size() != scaledImageRect.size()) { + return; + } + + TQ_INT32 imageWidth = imageSize.width(); + TQ_INT32 imageHeight = imageSize.height(); + + for (TQ_INT32 y = 0; y < scaledImageRect.height(); ++y) { + + TQ_INT32 scaledY = scaledImageRect.y() + y; + TQ_INT32 srcY = (scaledY * imageHeight) / scaledImageSize.height(); + TQ_INT32 patternY = srcY % PATTERN_HEIGHT; + + TQRgb *imagePixelPtr = reinterpret_cast(img.scanLine(y)); + const TQRgb *patternScanLine = reinterpret_cast(m_patternTile.scanLine(patternY)); + + for (TQ_INT32 x = 0; x < scaledImageRect.width(); ++x) { + + TQRgb imagePixel = *imagePixelPtr; + TQ_UINT8 imagePixelAlpha = tqAlpha(imagePixel); + + if (imagePixelAlpha != 255) { + + TQ_INT32 scaledX = scaledImageRect.x() + x; + TQ_INT32 srcX = (scaledX * imageWidth) / scaledImageSize.width(); + TQ_INT32 patternX = srcX % PATTERN_WIDTH; + + TQRgb patternPixel = patternScanLine[patternX]; + TQ_UINT8 imageRed = UINT8_BLEND(tqRed(imagePixel), tqRed(patternPixel), imagePixelAlpha); + TQ_UINT8 imageGreen = UINT8_BLEND(tqGreen(imagePixel), tqGreen(patternPixel), imagePixelAlpha); + TQ_UINT8 imageBlue = UINT8_BLEND(tqBlue(imagePixel), tqBlue(patternPixel), imagePixelAlpha); + + *imagePixelPtr = tqRgba(imageRed, imageGreen, imageBlue, 255); + } + + ++imagePixelPtr; + } + } +} + + diff --git a/chalk/core/kis_boundary.cc b/chalk/core/kis_boundary.cc deleted file mode 100644 index a15697a7..00000000 --- a/chalk/core/kis_boundary.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2005 Bart Coppens - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include - -#include "kis_colorspace.h" -#include "kis_iterators_pixel.h" -#include "kis_paint_device.h" -#include "kis_boundary.h" - -KisBoundary::KisBoundary(KisPaintDevice* dev) { - m_device = dev; - m_fuzzyness = 255 / 2; -} - -bool KisBoundary::isDark(TQ_UINT8 val) { - return val < m_fuzzyness; -} - -void KisBoundary::generateBoundary(int w, int h) { - if (!m_device) - return; - - KisColorSpace* cs = m_device->colorSpace(); - - // Horizontal - for (int currentY = - 1; currentY < h; currentY++) { - KisHLineIteratorPixel topIt = m_device->createHLineIterator(0, currentY, w, false); - KisHLineIteratorPixel botIt = m_device->createHLineIterator(0, currentY + 1, w, false); - bool darkTop; - bool darkBot; - - m_horSegments.append(TQValueList()); - - while (!topIt.isDone()) { - darkTop = cs->getAlpha(topIt.rawData()); - darkBot = cs->getAlpha(botIt.rawData()); - if (darkTop != darkBot) { - // detected a change - m_horSegments.back().append(tqMakePair(KisPoint(botIt.x(), botIt.y()), 1)); - } - ++topIt; - ++botIt; - } - } - - // Vertical - for (int currentX = - 1; currentX < w; currentX++) { - KisVLineIteratorPixel leftIt = m_device->createVLineIterator(currentX, 0, h, false); - KisVLineIteratorPixel rightIt = m_device->createVLineIterator(currentX + 1, 0, h, false); - bool darkLeft; - bool darkRight; - - m_vertSegments.append(TQValueList()); - - while (!leftIt.isDone()) { - darkLeft = cs->getAlpha(leftIt.rawData()); - darkRight = cs->getAlpha(rightIt.rawData()); - if (darkLeft != darkRight) { - // detected a change - m_vertSegments.back().append(tqMakePair(KisPoint(rightIt.x(), rightIt.y()), 1)); - } - ++leftIt; - ++rightIt; - } - } -} - diff --git a/chalk/core/kis_boundary.cpp b/chalk/core/kis_boundary.cpp new file mode 100644 index 00000000..a15697a7 --- /dev/null +++ b/chalk/core/kis_boundary.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2005 Bart Coppens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include + +#include "kis_colorspace.h" +#include "kis_iterators_pixel.h" +#include "kis_paint_device.h" +#include "kis_boundary.h" + +KisBoundary::KisBoundary(KisPaintDevice* dev) { + m_device = dev; + m_fuzzyness = 255 / 2; +} + +bool KisBoundary::isDark(TQ_UINT8 val) { + return val < m_fuzzyness; +} + +void KisBoundary::generateBoundary(int w, int h) { + if (!m_device) + return; + + KisColorSpace* cs = m_device->colorSpace(); + + // Horizontal + for (int currentY = - 1; currentY < h; currentY++) { + KisHLineIteratorPixel topIt = m_device->createHLineIterator(0, currentY, w, false); + KisHLineIteratorPixel botIt = m_device->createHLineIterator(0, currentY + 1, w, false); + bool darkTop; + bool darkBot; + + m_horSegments.append(TQValueList()); + + while (!topIt.isDone()) { + darkTop = cs->getAlpha(topIt.rawData()); + darkBot = cs->getAlpha(botIt.rawData()); + if (darkTop != darkBot) { + // detected a change + m_horSegments.back().append(tqMakePair(KisPoint(botIt.x(), botIt.y()), 1)); + } + ++topIt; + ++botIt; + } + } + + // Vertical + for (int currentX = - 1; currentX < w; currentX++) { + KisVLineIteratorPixel leftIt = m_device->createVLineIterator(currentX, 0, h, false); + KisVLineIteratorPixel rightIt = m_device->createVLineIterator(currentX + 1, 0, h, false); + bool darkLeft; + bool darkRight; + + m_vertSegments.append(TQValueList()); + + while (!leftIt.isDone()) { + darkLeft = cs->getAlpha(leftIt.rawData()); + darkRight = cs->getAlpha(rightIt.rawData()); + if (darkLeft != darkRight) { + // detected a change + m_vertSegments.back().append(tqMakePair(KisPoint(rightIt.x(), rightIt.y()), 1)); + } + ++leftIt; + ++rightIt; + } + } +} + diff --git a/chalk/core/kis_brush.cc b/chalk/core/kis_brush.cc deleted file mode 100644 index 49ee8e58..00000000 --- a/chalk/core/kis_brush.cc +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * Copyright (c) 1999 Matthias Elter - * Copyright (c) 2003 Patrick Julien - * Copyright (c) 2004 Boudewijn Rempt - * Copyright (c) 2004 Adrian Page - * Copyright (c) 2005 Bart Coppens - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include "kis_paint_device.h" -#include "kis_global.h" -#include "kis_brush.h" -#include "kis_alpha_mask.h" -#include "kis_colorspace_factory_registry.h" -#include "kis_iterators_pixel.h" -#include "kis_image.h" - - -namespace { - struct GimpBrushV1Header { - TQ_UINT32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ - TQ_UINT32 version; /* brush file version # */ - TQ_UINT32 width; /* width of brush */ - TQ_UINT32 height; /* height of brush */ - TQ_UINT32 bytes; /* depth of brush in bytes */ - }; - - /// All fields are in MSB on disk! - struct GimpBrushHeader { - TQ_UINT32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ - TQ_UINT32 version; /* brush file version # */ - TQ_UINT32 width; /* width of brush */ - TQ_UINT32 height; /* height of brush */ - TQ_UINT32 bytes; /* depth of brush in bytes */ - - /* The following are only defined in version 2 */ - TQ_UINT32 magic_number; /* GIMP brush magic number */ - TQ_UINT32 spacing; /* brush spacing as % of width & height, 0 - 1000 */ - }; - - // Needed, or the GIMP won't open it! - TQ_UINT32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0); -} - -#define DEFAULT_SPACING 0.25 -#define MAXIMUM_SCALE 2 - -KisBrush::KisBrush(const TQString& filename) : super(filename) -{ - m_brushType = INVALID; - m_ownData = true; - m_useColorAsMask = false; - m_hasColor = false; - m_spacing = DEFAULT_SPACING; - m_boundary = 0; -} - -KisBrush::KisBrush(const TQString& filename, - const TQByteArray& data, - TQ_UINT32 & dataPos) : super(filename) -{ - m_brushType = INVALID; - m_ownData = false; - m_useColorAsMask = false; - m_hasColor = false; - m_spacing = DEFAULT_SPACING; - m_boundary = 0; - - m_data.setRawData(data.data() + dataPos, data.size() - dataPos); - init(); - m_data.resetRawData(data.data() + dataPos, data.size() - dataPos); - dataPos += m_header_size + (width() * height() * m_bytes); -} - -KisBrush::KisBrush(KisPaintDevice* image, int x, int y, int w, int h) - : super(TQString("")) -{ - m_brushType = INVALID; - m_ownData = true; - m_useColorAsMask = false; - m_hasColor = true; - m_spacing = DEFAULT_SPACING; - m_boundary = 0; - - initFromPaintDev(image, x, y, w, h); -} - -KisBrush::KisBrush(const TQImage& image, const TQString& name) - : super(TQString("")) -{ - m_ownData = false; - m_useColorAsMask = false; - m_hasColor = true; - m_spacing = DEFAULT_SPACING; - m_boundary = 0; - - setImage(image); - setName(name); - setBrushType(IMAGE); -} - - -KisBrush::~KisBrush() -{ - m_scaledBrushes.clear(); - delete m_boundary; -} - -bool KisBrush::load() -{ - if (m_ownData) { - TQFile file(filename()); - file.open(IO_ReadOnly); - m_data = file.readAll(); - file.close(); - } - return init(); -} - -bool KisBrush::init() -{ - GimpBrushHeader bh; - - if (sizeof(GimpBrushHeader) > m_data.size()) { - return false; - } - - memcpy(&bh, &m_data[0], sizeof(GimpBrushHeader)); - bh.header_size = ntohl(bh.header_size); - m_header_size = bh.header_size; - - bh.version = ntohl(bh.version); - m_version = bh.version; - - bh.width = ntohl(bh.width); - bh.height = ntohl(bh.height); - - bh.bytes = ntohl(bh.bytes); - m_bytes = bh.bytes; - - bh.magic_number = ntohl(bh.magic_number); - m_magic_number = bh.magic_number; - - if (bh.version == 1) { - // No spacing in version 1 files so use Gimp default - bh.spacing = static_cast(DEFAULT_SPACING * 100); - } - else { - bh.spacing = ntohl(bh.spacing); - - if (bh.spacing > 1000) { - return false; - } - } - - setSpacing(bh.spacing / 100.0); - - if (bh.header_size > m_data.size() || bh.header_size == 0) { - return false; - } - - TQString name; - - if (bh.version == 1) { - // Version 1 has no magic number or spacing, so the name - // is at a different offset. Character encoding is undefined. - const char *text = &m_data[sizeof(GimpBrushV1Header)]; - name = TQString::fromAscii(text, bh.header_size - sizeof(GimpBrushV1Header)); - } else { - // ### Version = 3->cinepaint; may be float16 data! - // Version >=2: UTF-8 encoding is used - name = TQString::fromUtf8(&m_data[sizeof(GimpBrushHeader)], - bh.header_size - sizeof(GimpBrushHeader)); - } - - setName(i18n(name.ascii())); // Ascii? And what with real UTF-8 chars? - - if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) { - return false; - } - - TQ_INT32 k = bh.header_size; - - if (bh.bytes == 1) { - // Grayscale - - if (static_cast(k + bh.width * bh.height) > m_data.size()) { - return false; - } - - m_brushType = MASK; - m_hasColor = false; - - for (TQ_UINT32 y = 0; y < bh.height; y++) { - for (TQ_UINT32 x = 0; x < bh.width; x++, k++) { - TQ_INT32 val = 255 - static_cast(m_data[k]); - m_img.setPixel(x, y, tqRgb(val, val, val)); - } - } - } else if (bh.bytes == 4) { - // RGBA - - if (static_cast(k + (bh.width * bh.height * 4)) > m_data.size()) { - return false; - } - - m_brushType = IMAGE; - m_img.setAlphaBuffer(true); - m_hasColor = true; - - for (TQ_UINT32 y = 0; y < bh.height; y++) { - for (TQ_UINT32 x = 0; x < bh.width; x++, k += 4) { - m_img.setPixel(x, y, tqRgba(m_data[k], - m_data[k+1], - m_data[k+2], - m_data[k+3])); - } - } - } else { - return false; - } - - setWidth(m_img.width()); - setHeight(m_img.height()); - //createScaledBrushes(); - if (m_ownData) { - m_data.resize(0); // Save some memory, we're using enough of it as it is. - } - - - if (m_img.width() == 0 || m_img.height() == 0) - setValid(false); - else - setValid(true); - - return true; -} - -bool KisBrush::initFromPaintDev(KisPaintDevice* image, int x, int y, int w, int h) { - // Forcefully convert to RGBA8 - // XXX profile and exposure? - setImage(image->convertToTQImage(0, x, y, w, h)); - setName(image->name()); - - m_brushType = IMAGE; - m_hasColor = true; - - return true; -} - -bool KisBrush::save() -{ - TQFile file(filename()); - file.open(IO_WriteOnly | IO_Truncate); - bool ok = saveToDevice(TQT_TQIODEVICE(&file)); - file.close(); - return ok; -} - -bool KisBrush::saveToDevice(TQIODevice* dev) const -{ - GimpBrushHeader bh; - TQCString utf8Name = name().utf8(); // Names in v2 brushes are in UTF-8 - char const* name = utf8Name.data(); - int nameLength = tqstrlen(name); - int wrote; - - bh.header_size = htonl(sizeof(GimpBrushHeader) + nameLength); - bh.version = htonl(2); // Only RGBA8 data needed atm, no cinepaint stuff - bh.width = htonl(width()); - bh.height = htonl(height()); - // Hardcoded, 4 bytes RGBA or 1 byte GREY - if (!hasColor()) - bh.bytes = htonl(1); - else - bh.bytes = htonl(4); - bh.magic_number = htonl(GimpV2BrushMagic); - bh.spacing = htonl(static_cast(spacing() * 100.0)); - - // Write header: first bh, then the name - TQByteArray bytes; - bytes.setRawData(reinterpret_cast(&bh), sizeof(GimpBrushHeader)); - wrote = dev->writeBlock(bytes); - bytes.resetRawData(reinterpret_cast(&bh), sizeof(GimpBrushHeader)); - - if (wrote == -1) - return false; - - wrote = dev->writeBlock(name, nameLength); // No +1 for the trailing NULL it seems... - if (wrote == -1) - return false; - - int k = 0; - - if (!hasColor()) { - bytes.resize(width() * height()); - for (TQ_INT32 y = 0; y < height(); y++) { - for (TQ_INT32 x = 0; x < width(); x++) { - TQRgb c = m_img.pixel(x, y); - bytes[k++] = static_cast(255 - tqRed(c)); // red == blue == green - } - } - } else { - bytes.resize(width() * height() * 4); - for (TQ_INT32 y = 0; y < height(); y++) { - for (TQ_INT32 x = 0; x < width(); x++) { - // order for gimp brushes, v2 is: RGBA - TQRgb pixel = m_img.pixel(x,y); - bytes[k++] = static_cast(tqRed(pixel)); - bytes[k++] = static_cast(tqGreen(pixel)); - bytes[k++] = static_cast(tqBlue(pixel)); - bytes[k++] = static_cast(tqAlpha(pixel)); - } - } - } - - wrote = dev->writeBlock(bytes); - if (wrote == -1) - return false; - - return true; -} - -TQImage KisBrush::img() -{ - TQImage image = m_img; - - if (hasColor() && useColorAsMask()) { - image.detach(); - - for (int x = 0; x < image.width(); x++) { - for (int y = 0; y < image.height(); y++) { - TQRgb c = image.pixel(x, y); - int a = (tqGray(c) * tqAlpha(c)) / 255; - image.setPixel(x, y, tqRgba(a, 0, a, a)); - } - } - } - - return image; -} - -KisAlphaMaskSP KisBrush::mask(const KisPaintInformation& info, double subPixelX, double subPixelY) const -{ - if (m_scaledBrushes.isEmpty()) { - createScaledBrushes(); - } - - double scale = scaleForPressure(info.pressure); - - const ScaledBrush *aboveBrush = 0; - const ScaledBrush *belowBrush = 0; - - findScaledBrushes(scale, &aboveBrush, &belowBrush); - Q_ASSERT(aboveBrush != 0); - - KisAlphaMaskSP outputMask = 0; - - if (belowBrush != 0) { - // We're in between two masks. Interpolate between them. - - KisAlphaMaskSP scaledAboveMask = scaleMask(aboveBrush, scale, subPixelX, subPixelY); - KisAlphaMaskSP scaledBelowMask = scaleMask(belowBrush, scale, subPixelX, subPixelY); - - double t = (scale - belowBrush->scale()) / (aboveBrush->scale() - belowBrush->scale()); - - outputMask = KisAlphaMask::interpolate(scaledBelowMask, scaledAboveMask, t); - } else { - if (fabs(scale - aboveBrush->scale()) < DBL_EPSILON) { - // Exact match. - outputMask = scaleMask(aboveBrush, scale, subPixelX, subPixelY); - } else { - // We are smaller than the smallest mask, which is always 1x1. - double s = scale / aboveBrush->scale(); - outputMask = scaleSinglePixelMask(s, aboveBrush->mask()->alphaAt(0, 0), subPixelX, subPixelY); - } - } - - return outputMask; -} - -KisPaintDeviceSP KisBrush::image(KisColorSpace * /*colorSpace*/, const KisPaintInformation& info, double subPixelX, double subPixelY) const -{ - if (m_scaledBrushes.isEmpty()) { - createScaledBrushes(); - } - - double scale = scaleForPressure(info.pressure); - - const ScaledBrush *aboveBrush = 0; - const ScaledBrush *belowBrush = 0; - - findScaledBrushes(scale, &aboveBrush, &belowBrush); - Q_ASSERT(aboveBrush != 0); - - TQImage outputImage; - - if (belowBrush != 0) { - // We're in between two brushes. Interpolate between them. - - TQImage scaledAboveImage = scaleImage(aboveBrush, scale, subPixelX, subPixelY); - TQImage scaledBelowImage = scaleImage(belowBrush, scale, subPixelX, subPixelY); - - double t = (scale - belowBrush->scale()) / (aboveBrush->scale() - belowBrush->scale()); - - outputImage = interpolate(scaledBelowImage, scaledAboveImage, t); - } else { - if (fabs(scale - aboveBrush->scale()) < DBL_EPSILON) { - // Exact match. - outputImage = scaleImage(aboveBrush, scale, subPixelX, subPixelY); - } else { - // We are smaller than the smallest brush, which is always 1x1. - double s = scale / aboveBrush->scale(); - outputImage = scaleSinglePixelImage(s, aboveBrush->image().pixel(0, 0), subPixelX, subPixelY); - } - } - - int outputWidth = outputImage.width(); - int outputHeight = outputImage.height(); - - KisPaintDevice *layer = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "brush"); - - TQ_CHECK_PTR(layer); - - for (int y = 0; y < outputHeight; y++) { - KisHLineIterator iter = layer->createHLineIterator( 0, y, outputWidth, true); - for (int x = 0; x < outputWidth; x++) { - TQ_UINT8 * p = iter.rawData(); - - TQRgb pixel = outputImage.pixel(x, y); - int red = tqRed(pixel); - int green = tqGreen(pixel); - int blue = tqBlue(pixel); - int alpha = tqAlpha(pixel); - - // Scaled images are in pre-multiplied alpha form so - // divide by alpha. - // channel order is BGRA - if (alpha != 0) { - p[2] = (red * 255) / alpha; - p[1] = (green * 255) / alpha; - p[0] = (blue * 255) / alpha; - p[3] = alpha; - } - - ++iter; - } - } - - return layer; -} - -void KisBrush::setHotSpot(KisPoint pt) -{ - double x = pt.x(); - double y = pt.y(); - - if (x < 0) - x = 0; - else if (x >= width()) - x = width() - 1; - - if (y < 0) - y = 0; - else if (y >= height()) - y = height() - 1; - - m_hotSpot = KisPoint(x, y); -} - -KisPoint KisBrush::hotSpot(const KisPaintInformation& info) const -{ - double scale = scaleForPressure(info.pressure); - double w = width() * scale; - double h = height() * scale; - - // The smallest brush we can produce is a single pixel. - if (w < 1) { - w = 1; - } - - if (h < 1) { - h = 1; - } - - // XXX: This should take m_hotSpot into account, though it - // isn't specified by gimp brushes so it would default to the centre - // anyway. - KisPoint p(w / 2, h / 2); - return p; -} - -enumBrushType KisBrush::brushType() const -{ - if (m_brushType == IMAGE && useColorAsMask()) { - return MASK; - } - else { - return m_brushType; - } -} - -bool KisBrush::hasColor() const -{ - return m_hasColor; -} - -void KisBrush::createScaledBrushes() const -{ - if (!m_scaledBrushes.isEmpty()) - m_scaledBrushes.clear(); - - // Construct a series of brushes where each one's dimensions are - // half the size of the previous one. - int width = m_img.width() * MAXIMUM_SCALE; - int height = m_img.height() * MAXIMUM_SCALE; - - TQImage scaledImage; - - while (true) { - - if (width >= m_img.width() && height >= m_img.height()) { - scaledImage = scaleImage(m_img, width, height); - } - else { - // Scale down the previous image once we're below 1:1. - scaledImage = scaleImage(scaledImage, width, height); - } - - KisAlphaMaskSP scaledMask = new KisAlphaMask(scaledImage, hasColor()); - TQ_CHECK_PTR(scaledMask); - - double xScale = static_cast(width) / m_img.width(); - double yScale = static_cast(height) / m_img.height(); - double scale = xScale; - - m_scaledBrushes.append(ScaledBrush(scaledMask, hasColor() ? scaledImage : TQImage(), scale, xScale, yScale)); - - if (width == 1 && height == 1) { - break; - } - - // Round up so that we never have to scale an image by less than 1/2. - width = (width + 1) / 2; - height = (height + 1) / 2; - - } - -} - -double KisBrush::xSpacing(double pressure) const -{ - return width() * scaleForPressure(pressure) * m_spacing; -} - -double KisBrush::ySpacing(double pressure) const -{ - return height() * scaleForPressure(pressure) * m_spacing; -} - -double KisBrush::scaleForPressure(double pressure) -{ - double scale = pressure / PRESSURE_DEFAULT; - - if (scale < 0) { - scale = 0; - } - - if (scale > MAXIMUM_SCALE) { - scale = MAXIMUM_SCALE; - } - - return scale; -} - -TQ_INT32 KisBrush::maskWidth(const KisPaintInformation& info) const -{ - // Add one for sub-pixel shift - return static_cast(ceil(width() * scaleForPressure(info.pressure)) + 1); -} - -TQ_INT32 KisBrush::maskHeight(const KisPaintInformation& info) const -{ - // Add one for sub-pixel shift - return static_cast(ceil(height() * scaleForPressure(info.pressure)) + 1); -} - -KisAlphaMaskSP KisBrush::scaleMask(const ScaledBrush *srcBrush, double scale, double subPixelX, double subPixelY) const -{ - // Add one pixel for sub-pixel shifting - int dstWidth = static_cast(ceil(scale * width())) + 1; - int dstHeight = static_cast(ceil(scale * height())) + 1; - - KisAlphaMaskSP dstMask = new KisAlphaMask(dstWidth, dstHeight); - TQ_CHECK_PTR(dstMask); - - KisAlphaMaskSP srcMask = srcBrush->mask(); - - // Compute scales to map the scaled brush onto the required scale. - double xScale = srcBrush->xScale() / scale; - double yScale = srcBrush->yScale() / scale; - - int srcWidth = srcMask->width(); - int srcHeight = srcMask->height(); - - for (int dstY = 0; dstY < dstHeight; dstY++) { - for (int dstX = 0; dstX < dstWidth; dstX++) { - - double srcX = (dstX - subPixelX + 0.5) * xScale; - double srcY = (dstY - subPixelY + 0.5) * yScale; - - srcX -= 0.5; - srcY -= 0.5; - - int leftX = static_cast(srcX); - - if (srcX < 0) { - leftX--; - } - - double xInterp = srcX - leftX; - - int topY = static_cast(srcY); - - if (srcY < 0) { - topY--; - } - - double yInterp = srcY - topY; - - TQ_UINT8 topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcMask->alphaAt(leftX, topY) : OPACITY_TRANSPARENT; - TQ_UINT8 bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcMask->alphaAt(leftX, topY + 1) : OPACITY_TRANSPARENT; - TQ_UINT8 topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcMask->alphaAt(leftX + 1, topY) : OPACITY_TRANSPARENT; - TQ_UINT8 bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcMask->alphaAt(leftX + 1, topY + 1) : OPACITY_TRANSPARENT; - - double a = 1 - xInterp; - double b = 1 - yInterp; - - // Bi-linear interpolation - int d = static_cast(a * b * topLeft - + a * (1 - b) * bottomLeft - + (1 - a) * b * topRight - + (1 - a) * (1 - b) * bottomRight + 0.5); - - if (d < OPACITY_TRANSPARENT) { - d = OPACITY_TRANSPARENT; - } - else - if (d > OPACITY_OPAQUE) { - d = OPACITY_OPAQUE; - } - - dstMask->setAlphaAt(dstX, dstY, static_cast(d)); - } - } - - return dstMask; -} - -TQImage KisBrush::scaleImage(const ScaledBrush *srcBrush, double scale, double subPixelX, double subPixelY) const -{ - // Add one pixel for sub-pixel shifting - int dstWidth = static_cast(ceil(scale * width())) + 1; - int dstHeight = static_cast(ceil(scale * height())) + 1; - - TQImage dstImage(dstWidth, dstHeight, 32); - dstImage.setAlphaBuffer(true); - - const TQImage srcImage = srcBrush->image(); - - // Compute scales to map the scaled brush onto the required scale. - double xScale = srcBrush->xScale() / scale; - double yScale = srcBrush->yScale() / scale; - - int srcWidth = srcImage.width(); - int srcHeight = srcImage.height(); - - for (int dstY = 0; dstY < dstHeight; dstY++) { - for (int dstX = 0; dstX < dstWidth; dstX++) { - - double srcX = (dstX - subPixelX + 0.5) * xScale; - double srcY = (dstY - subPixelY + 0.5) * yScale; - - srcX -= 0.5; - srcY -= 0.5; - - int leftX = static_cast(srcX); - - if (srcX < 0) { - leftX--; - } - - double xInterp = srcX - leftX; - - int topY = static_cast(srcY); - - if (srcY < 0) { - topY--; - } - - double yInterp = srcY - topY; - - TQRgb topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX, topY) : tqRgba(0, 0, 0, 0); - TQRgb bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX, topY + 1) : tqRgba(0, 0, 0, 0); - TQRgb topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX + 1, topY) : tqRgba(0, 0, 0, 0); - TQRgb bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX + 1, topY + 1) : tqRgba(0, 0, 0, 0); - - double a = 1 - xInterp; - double b = 1 - yInterp; - - // Bi-linear interpolation. Image is pre-multiplied by alpha. - int red = static_cast(a * b * tqRed(topLeft) - + a * (1 - b) * tqRed(bottomLeft) - + (1 - a) * b * tqRed(topRight) - + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5); - int green = static_cast(a * b * tqGreen(topLeft) - + a * (1 - b) * tqGreen(bottomLeft) - + (1 - a) * b * tqGreen(topRight) - + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5); - int blue = static_cast(a * b * tqBlue(topLeft) - + a * (1 - b) * tqBlue(bottomLeft) - + (1 - a) * b * tqBlue(topRight) - + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5); - int alpha = static_cast(a * b * tqAlpha(topLeft) - + a * (1 - b) * tqAlpha(bottomLeft) - + (1 - a) * b * tqAlpha(topRight) - + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5); - - if (red < 0) { - red = 0; - } - else - if (red > 255) { - red = 255; - } - - if (green < 0) { - green = 0; - } - else - if (green > 255) { - green = 255; - } - - if (blue < 0) { - blue = 0; - } - else - if (blue > 255) { - blue = 255; - } - - if (alpha < 0) { - alpha = 0; - } - else - if (alpha > 255) { - alpha = 255; - } - - dstImage.setPixel(dstX, dstY, tqRgba(red, green, blue, alpha)); - } - } - - return dstImage; -} - -TQImage KisBrush::scaleImage(const TQImage& srcImage, int width, int height) -{ - TQImage scaledImage; - //TQString filename; - - int srcWidth = srcImage.width(); - int srcHeight = srcImage.height(); - - double xScale = static_cast(srcWidth) / width; - double yScale = static_cast(srcHeight) / height; - - if (xScale > 2 + DBL_EPSILON || yScale > 2 + DBL_EPSILON || xScale < 1 - DBL_EPSILON || yScale < 1 - DBL_EPSILON) { - // smoothScale gives better results when scaling an image up - // or scaling it to less than half size. - scaledImage = srcImage.smoothScale(width, height); - - //filename = TQString("smoothScale_%1x%2.png").arg(width).arg(height); - } - else { - scaledImage.create(width, height, 32); - scaledImage.setAlphaBuffer(srcImage.hasAlphaBuffer()); - - for (int dstY = 0; dstY < height; dstY++) { - for (int dstX = 0; dstX < width; dstX++) { - - double srcX = (dstX + 0.5) * xScale; - double srcY = (dstY + 0.5) * yScale; - - srcX -= 0.5; - srcY -= 0.5; - - int leftX = static_cast(srcX); - - if (srcX < 0) { - leftX--; - } - - double xInterp = srcX - leftX; - - int topY = static_cast(srcY); - - if (srcY < 0) { - topY--; - } - - double yInterp = srcY - topY; - - TQRgb topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX, topY) : tqRgba(0, 0, 0, 0); - TQRgb bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX, topY + 1) : tqRgba(0, 0, 0, 0); - TQRgb topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX + 1, topY) : tqRgba(0, 0, 0, 0); - TQRgb bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX + 1, topY + 1) : tqRgba(0, 0, 0, 0); - - double a = 1 - xInterp; - double b = 1 - yInterp; - - int red; - int green; - int blue; - int alpha; - - if (srcImage.hasAlphaBuffer()) { - red = static_cast(a * b * tqRed(topLeft) * tqAlpha(topLeft) - + a * (1 - b) * tqRed(bottomLeft) * tqAlpha(bottomLeft) - + (1 - a) * b * tqRed(topRight) * tqAlpha(topRight) - + (1 - a) * (1 - b) * tqRed(bottomRight) * tqAlpha(bottomRight) + 0.5); - green = static_cast(a * b * tqGreen(topLeft) * tqAlpha(topLeft) - + a * (1 - b) * tqGreen(bottomLeft) * tqAlpha(bottomLeft) - + (1 - a) * b * tqGreen(topRight) * tqAlpha(topRight) - + (1 - a) * (1 - b) * tqGreen(bottomRight) * tqAlpha(bottomRight) + 0.5); - blue = static_cast(a * b * tqBlue(topLeft) * tqAlpha(topLeft) - + a * (1 - b) * tqBlue(bottomLeft) * tqAlpha(bottomLeft) - + (1 - a) * b * tqBlue(topRight) * tqAlpha(topRight) - + (1 - a) * (1 - b) * tqBlue(bottomRight) * tqAlpha(bottomRight) + 0.5); - alpha = static_cast(a * b * tqAlpha(topLeft) - + a * (1 - b) * tqAlpha(bottomLeft) - + (1 - a) * b * tqAlpha(topRight) - + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5); - - if (alpha != 0) { - red /= alpha; - green /= alpha; - blue /= alpha; - } - } - else { - red = static_cast(a * b * tqRed(topLeft) - + a * (1 - b) * tqRed(bottomLeft) - + (1 - a) * b * tqRed(topRight) - + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5); - green = static_cast(a * b * tqGreen(topLeft) - + a * (1 - b) * tqGreen(bottomLeft) - + (1 - a) * b * tqGreen(topRight) - + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5); - blue = static_cast(a * b * tqBlue(topLeft) - + a * (1 - b) * tqBlue(bottomLeft) - + (1 - a) * b * tqBlue(topRight) - + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5); - alpha = 255; - } - - if (red < 0) { - red = 0; - } - else - if (red > 255) { - red = 255; - } - - if (green < 0) { - green = 0; - } - else - if (green > 255) { - green = 255; - } - - if (blue < 0) { - blue = 0; - } - else - if (blue > 255) { - blue = 255; - } - - if (alpha < 0) { - alpha = 0; - } - else - if (alpha > 255) { - alpha = 255; - } - - scaledImage.setPixel(dstX, dstY, tqRgba(red, green, blue, alpha)); - } - } - - //filename = TQString("bilinear_%1x%2.png").arg(width).arg(height); - } - - //scaledImage.save(filename, "PNG"); - - return scaledImage; -} - -void KisBrush::findScaledBrushes(double scale, const ScaledBrush **aboveBrush, const ScaledBrush **belowBrush) const -{ - uint current = 0; - - while (true) { - *aboveBrush = &(m_scaledBrushes[current]); - - if (fabs((*aboveBrush)->scale() - scale) < DBL_EPSILON) { - // Scale matches exactly - break; - } - - if (current == m_scaledBrushes.count() - 1) { - // This is the last one - break; - } - - if (scale > m_scaledBrushes[current + 1].scale() + DBL_EPSILON) { - // We fit in between the two. - *belowBrush = &(m_scaledBrushes[current + 1]); - break; - } - - current++; - } -} - -KisAlphaMaskSP KisBrush::scaleSinglePixelMask(double scale, TQ_UINT8 maskValue, double subPixelX, double subPixelY) -{ - int srcWidth = 1; - int srcHeight = 1; - int dstWidth = 2; - int dstHeight = 2; - KisAlphaMaskSP outputMask = new KisAlphaMask(dstWidth, dstHeight); - TQ_CHECK_PTR(outputMask); - - double a = subPixelX; - double b = subPixelY; - - for (int y = 0; y < dstHeight; y++) { - for (int x = 0; x < dstWidth; x++) { - - TQ_UINT8 topLeft = (x > 0 && y > 0) ? maskValue : OPACITY_TRANSPARENT; - TQ_UINT8 bottomLeft = (x > 0 && y < srcHeight) ? maskValue : OPACITY_TRANSPARENT; - TQ_UINT8 topRight = (x < srcWidth && y > 0) ? maskValue : OPACITY_TRANSPARENT; - TQ_UINT8 bottomRight = (x < srcWidth && y < srcHeight) ? maskValue : OPACITY_TRANSPARENT; - - // Bi-linear interpolation - int d = static_cast(a * b * topLeft - + a * (1 - b) * bottomLeft - + (1 - a) * b * topRight - + (1 - a) * (1 - b) * bottomRight + 0.5); - - // Multiply by the square of the scale because a 0.5x0.5 pixel - // has 0.25 the value of the 1x1. - d = static_cast(d * scale * scale + 0.5); - - if (d < OPACITY_TRANSPARENT) { - d = OPACITY_TRANSPARENT; - } - else - if (d > OPACITY_OPAQUE) { - d = OPACITY_OPAQUE; - } - - outputMask->setAlphaAt(x, y, static_cast(d)); - } - } - - return outputMask; -} - -TQImage KisBrush::scaleSinglePixelImage(double scale, TQRgb pixel, double subPixelX, double subPixelY) -{ - int srcWidth = 1; - int srcHeight = 1; - int dstWidth = 2; - int dstHeight = 2; - - TQImage outputImage(dstWidth, dstHeight, 32); - outputImage.setAlphaBuffer(true); - - double a = subPixelX; - double b = subPixelY; - - for (int y = 0; y < dstHeight; y++) { - for (int x = 0; x < dstWidth; x++) { - - TQRgb topLeft = (x > 0 && y > 0) ? pixel : tqRgba(0, 0, 0, 0); - TQRgb bottomLeft = (x > 0 && y < srcHeight) ? pixel : tqRgba(0, 0, 0, 0); - TQRgb topRight = (x < srcWidth && y > 0) ? pixel : tqRgba(0, 0, 0, 0); - TQRgb bottomRight = (x < srcWidth && y < srcHeight) ? pixel : tqRgba(0, 0, 0, 0); - - // Bi-linear interpolation. Images are in pre-multiplied form. - int red = static_cast(a * b * tqRed(topLeft) - + a * (1 - b) * tqRed(bottomLeft) - + (1 - a) * b * tqRed(topRight) - + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5); - int green = static_cast(a * b * tqGreen(topLeft) - + a * (1 - b) * tqGreen(bottomLeft) - + (1 - a) * b * tqGreen(topRight) - + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5); - int blue = static_cast(a * b * tqBlue(topLeft) - + a * (1 - b) * tqBlue(bottomLeft) - + (1 - a) * b * tqBlue(topRight) - + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5); - int alpha = static_cast(a * b * tqAlpha(topLeft) - + a * (1 - b) * tqAlpha(bottomLeft) - + (1 - a) * b * tqAlpha(topRight) - + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5); - - // Multiply by the square of the scale because a 0.5x0.5 pixel - // has 0.25 the value of the 1x1. - alpha = static_cast(alpha * scale * scale + 0.5); - - // Apply to the colour channels too since we are - // storing pre-multiplied by alpha. - red = static_cast(red * scale * scale + 0.5); - green = static_cast(green * scale * scale + 0.5); - blue = static_cast(blue * scale * scale + 0.5); - - if (red < 0) { - red = 0; - } - else - if (red > 255) { - red = 255; - } - - if (green < 0) { - green = 0; - } - else - if (green > 255) { - green = 255; - } - - if (blue < 0) { - blue = 0; - } - else - if (blue > 255) { - blue = 255; - } - - if (alpha < 0) { - alpha = 0; - } - else - if (alpha > 255) { - alpha = 255; - } - - outputImage.setPixel(x, y, tqRgba(red, green, blue, alpha)); - } - } - - return outputImage; -} - -TQImage KisBrush::interpolate(const TQImage& image1, const TQImage& image2, double t) -{ - Q_ASSERT((image1.width() == image2.width()) && (image1.height() == image2.height())); - Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); - - int width = image1.width(); - int height = image1.height(); - - TQImage outputImage(width, height, 32); - outputImage.setAlphaBuffer(true); - - for (int x = 0; x < width; x++) { - for (int y = 0; y < height; y++) { - TQRgb image1pixel = image1.pixel(x, y); - TQRgb image2pixel = image2.pixel(x, y); - - // Images are in pre-multiplied alpha format. - int red = static_cast((1 - t) * tqRed(image1pixel) + t * tqRed(image2pixel) + 0.5); - int green = static_cast((1 - t) * tqGreen(image1pixel) + t * tqGreen(image2pixel) + 0.5); - int blue = static_cast((1 - t) * tqBlue(image1pixel) + t * tqBlue(image2pixel) + 0.5); - int alpha = static_cast((1 - t) * tqAlpha(image1pixel) + t * tqAlpha(image2pixel) + 0.5); - - if (red < 0) { - red = 0; - } - else - if (red > 255) { - red = 255; - } - - if (green < 0) { - green = 0; - } - else - if (green > 255) { - green = 255; - } - - if (blue < 0) { - blue = 0; - } - else - if (blue > 255) { - blue = 255; - } - - if (alpha < 0) { - alpha = 0; - } - else - if (alpha > 255) { - alpha = 255; - } - - outputImage.setPixel(x, y, tqRgba(red, green, blue, alpha)); - } - } - - return outputImage; -} - -KisBrush::ScaledBrush::ScaledBrush() -{ - m_mask = 0; - m_image = TQImage(); - m_scale = 1; - m_xScale = 1; - m_yScale = 1; -} - -KisBrush::ScaledBrush::ScaledBrush(KisAlphaMaskSP scaledMask, const TQImage& scaledImage, double scale, double xScale, double yScale) -{ - m_mask = scaledMask; - m_image = scaledImage; - m_scale = scale; - m_xScale = xScale; - m_yScale = yScale; - - if (!m_image.isNull()) { - // Convert image to pre-multiplied by alpha. - - m_image.detach(); - - for (int y = 0; y < m_image.height(); y++) { - for (int x = 0; x < m_image.width(); x++) { - - TQRgb pixel = m_image.pixel(x, y); - - int red = tqRed(pixel); - int green = tqGreen(pixel); - int blue = tqBlue(pixel); - int alpha = tqAlpha(pixel); - - red = (red * alpha) / 255; - green = (green * alpha) / 255; - blue = (blue * alpha) / 255; - - m_image.setPixel(x, y, tqRgba(red, green, blue, alpha)); - } - } - } -} - -void KisBrush::setImage(const TQImage& img) -{ - m_img = img; - m_img.detach(); - - setWidth(img.width()); - setHeight(img.height()); - - m_scaledBrushes.clear(); - - setValid(true); -} - -TQ_INT32 KisBrush::width() const -{ - return m_width; -} - -void KisBrush::setWidth(TQ_INT32 w) -{ - m_width = w; -} - -TQ_INT32 KisBrush::height() const -{ - return m_height; -} - -void KisBrush::setHeight(TQ_INT32 h) -{ - m_height = h; -} - -/*TQImage KisBrush::outline(double pressure) { - KisLayerSP layer = image(KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""), - KisPaintInformation(pressure)); - KisBoundary bounds(layer.data()); - int w = maskWidth(pressure); - int h = maskHeight(pressure); - - bounds.generateBoundary(w, h); - TQPixmap pix(bounds.pixmap(w, h)); - TQImage result; - result = pix; - return result; -}*/ - -void KisBrush::generateBoundary() { - KisPaintDeviceSP dev; - int w = maskWidth(KisPaintInformation()); - int h = maskHeight(KisPaintInformation()); - - if (brushType() == IMAGE || brushType() == PIPE_IMAGE) { - dev = image(KisMetaRegistry::instance()->csRegistry() ->getColorSpace(KisID("RGBA",""),""), KisPaintInformation()); - } else { - KisAlphaMaskSP amask = mask(KisPaintInformation()); - KisColorSpace* cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""); - dev = new KisPaintDevice(cs, "tmp for generateBoundary"); - for (int y = 0; y < h; y++) { - KisHLineIteratorPixel it = dev->createHLineIterator(0, y, w, true); - int x = 0; - - while(!it.isDone()) { - cs->setAlpha(it.rawData(), amask->alphaAt(x++, y), 1); - ++it; - } - } - } - - m_boundary = new KisBoundary(dev); - m_boundary->generateBoundary(w, h); -} - -KisBoundary KisBrush::boundary() { - if (!m_boundary) - generateBoundary(); - return *m_boundary; -} - -void KisBrush::makeMaskImage() { - if (!hasColor()) - return; - - TQImage img; - img.create(width(), height(), 32); - - if (m_img.width() == img.width() && m_img.height() == img.height()) { - for (int x = 0; x < width(); x++) { - for (int y = 0; y < height(); y++) { - TQRgb c = m_img.pixel(x, y); - int a = (tqGray(c) * tqAlpha(c)) / 255; // tqGray(black) = 0 - img.setPixel(x, y, tqRgba(a, a, a, 255)); - } - } - - m_img = img; - } - - m_brushType = MASK; - m_hasColor = false; - m_useColorAsMask = false; - delete m_boundary; - m_boundary = 0; - m_scaledBrushes.clear(); -} - -KisBrush* KisBrush::clone() const { - KisBrush* c = new KisBrush(""); - c->m_spacing = m_spacing; - c->m_useColorAsMask = m_useColorAsMask; - c->m_hasColor = m_useColorAsMask; - c->m_img = m_img; - c->m_width = m_width; - c->m_height = m_height; - c->m_ownData = false; - c->m_hotSpot = m_hotSpot; - c->m_brushType = m_brushType; - c->setValid(true); - - return c; -} - -#include "kis_brush.moc" - diff --git a/chalk/core/kis_brush.cpp b/chalk/core/kis_brush.cpp new file mode 100644 index 00000000..49ee8e58 --- /dev/null +++ b/chalk/core/kis_brush.cpp @@ -0,0 +1,1333 @@ +/* + * Copyright (c) 1999 Matthias Elter + * Copyright (c) 2003 Patrick Julien + * Copyright (c) 2004 Boudewijn Rempt + * Copyright (c) 2004 Adrian Page + * Copyright (c) 2005 Bart Coppens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include "kis_paint_device.h" +#include "kis_global.h" +#include "kis_brush.h" +#include "kis_alpha_mask.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_iterators_pixel.h" +#include "kis_image.h" + + +namespace { + struct GimpBrushV1Header { + TQ_UINT32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ + TQ_UINT32 version; /* brush file version # */ + TQ_UINT32 width; /* width of brush */ + TQ_UINT32 height; /* height of brush */ + TQ_UINT32 bytes; /* depth of brush in bytes */ + }; + + /// All fields are in MSB on disk! + struct GimpBrushHeader { + TQ_UINT32 header_size; /* header_size = sizeof (BrushHeader) + brush name */ + TQ_UINT32 version; /* brush file version # */ + TQ_UINT32 width; /* width of brush */ + TQ_UINT32 height; /* height of brush */ + TQ_UINT32 bytes; /* depth of brush in bytes */ + + /* The following are only defined in version 2 */ + TQ_UINT32 magic_number; /* GIMP brush magic number */ + TQ_UINT32 spacing; /* brush spacing as % of width & height, 0 - 1000 */ + }; + + // Needed, or the GIMP won't open it! + TQ_UINT32 const GimpV2BrushMagic = ('G' << 24) + ('I' << 16) + ('M' << 8) + ('P' << 0); +} + +#define DEFAULT_SPACING 0.25 +#define MAXIMUM_SCALE 2 + +KisBrush::KisBrush(const TQString& filename) : super(filename) +{ + m_brushType = INVALID; + m_ownData = true; + m_useColorAsMask = false; + m_hasColor = false; + m_spacing = DEFAULT_SPACING; + m_boundary = 0; +} + +KisBrush::KisBrush(const TQString& filename, + const TQByteArray& data, + TQ_UINT32 & dataPos) : super(filename) +{ + m_brushType = INVALID; + m_ownData = false; + m_useColorAsMask = false; + m_hasColor = false; + m_spacing = DEFAULT_SPACING; + m_boundary = 0; + + m_data.setRawData(data.data() + dataPos, data.size() - dataPos); + init(); + m_data.resetRawData(data.data() + dataPos, data.size() - dataPos); + dataPos += m_header_size + (width() * height() * m_bytes); +} + +KisBrush::KisBrush(KisPaintDevice* image, int x, int y, int w, int h) + : super(TQString("")) +{ + m_brushType = INVALID; + m_ownData = true; + m_useColorAsMask = false; + m_hasColor = true; + m_spacing = DEFAULT_SPACING; + m_boundary = 0; + + initFromPaintDev(image, x, y, w, h); +} + +KisBrush::KisBrush(const TQImage& image, const TQString& name) + : super(TQString("")) +{ + m_ownData = false; + m_useColorAsMask = false; + m_hasColor = true; + m_spacing = DEFAULT_SPACING; + m_boundary = 0; + + setImage(image); + setName(name); + setBrushType(IMAGE); +} + + +KisBrush::~KisBrush() +{ + m_scaledBrushes.clear(); + delete m_boundary; +} + +bool KisBrush::load() +{ + if (m_ownData) { + TQFile file(filename()); + file.open(IO_ReadOnly); + m_data = file.readAll(); + file.close(); + } + return init(); +} + +bool KisBrush::init() +{ + GimpBrushHeader bh; + + if (sizeof(GimpBrushHeader) > m_data.size()) { + return false; + } + + memcpy(&bh, &m_data[0], sizeof(GimpBrushHeader)); + bh.header_size = ntohl(bh.header_size); + m_header_size = bh.header_size; + + bh.version = ntohl(bh.version); + m_version = bh.version; + + bh.width = ntohl(bh.width); + bh.height = ntohl(bh.height); + + bh.bytes = ntohl(bh.bytes); + m_bytes = bh.bytes; + + bh.magic_number = ntohl(bh.magic_number); + m_magic_number = bh.magic_number; + + if (bh.version == 1) { + // No spacing in version 1 files so use Gimp default + bh.spacing = static_cast(DEFAULT_SPACING * 100); + } + else { + bh.spacing = ntohl(bh.spacing); + + if (bh.spacing > 1000) { + return false; + } + } + + setSpacing(bh.spacing / 100.0); + + if (bh.header_size > m_data.size() || bh.header_size == 0) { + return false; + } + + TQString name; + + if (bh.version == 1) { + // Version 1 has no magic number or spacing, so the name + // is at a different offset. Character encoding is undefined. + const char *text = &m_data[sizeof(GimpBrushV1Header)]; + name = TQString::fromAscii(text, bh.header_size - sizeof(GimpBrushV1Header)); + } else { + // ### Version = 3->cinepaint; may be float16 data! + // Version >=2: UTF-8 encoding is used + name = TQString::fromUtf8(&m_data[sizeof(GimpBrushHeader)], + bh.header_size - sizeof(GimpBrushHeader)); + } + + setName(i18n(name.ascii())); // Ascii? And what with real UTF-8 chars? + + if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) { + return false; + } + + TQ_INT32 k = bh.header_size; + + if (bh.bytes == 1) { + // Grayscale + + if (static_cast(k + bh.width * bh.height) > m_data.size()) { + return false; + } + + m_brushType = MASK; + m_hasColor = false; + + for (TQ_UINT32 y = 0; y < bh.height; y++) { + for (TQ_UINT32 x = 0; x < bh.width; x++, k++) { + TQ_INT32 val = 255 - static_cast(m_data[k]); + m_img.setPixel(x, y, tqRgb(val, val, val)); + } + } + } else if (bh.bytes == 4) { + // RGBA + + if (static_cast(k + (bh.width * bh.height * 4)) > m_data.size()) { + return false; + } + + m_brushType = IMAGE; + m_img.setAlphaBuffer(true); + m_hasColor = true; + + for (TQ_UINT32 y = 0; y < bh.height; y++) { + for (TQ_UINT32 x = 0; x < bh.width; x++, k += 4) { + m_img.setPixel(x, y, tqRgba(m_data[k], + m_data[k+1], + m_data[k+2], + m_data[k+3])); + } + } + } else { + return false; + } + + setWidth(m_img.width()); + setHeight(m_img.height()); + //createScaledBrushes(); + if (m_ownData) { + m_data.resize(0); // Save some memory, we're using enough of it as it is. + } + + + if (m_img.width() == 0 || m_img.height() == 0) + setValid(false); + else + setValid(true); + + return true; +} + +bool KisBrush::initFromPaintDev(KisPaintDevice* image, int x, int y, int w, int h) { + // Forcefully convert to RGBA8 + // XXX profile and exposure? + setImage(image->convertToTQImage(0, x, y, w, h)); + setName(image->name()); + + m_brushType = IMAGE; + m_hasColor = true; + + return true; +} + +bool KisBrush::save() +{ + TQFile file(filename()); + file.open(IO_WriteOnly | IO_Truncate); + bool ok = saveToDevice(TQT_TQIODEVICE(&file)); + file.close(); + return ok; +} + +bool KisBrush::saveToDevice(TQIODevice* dev) const +{ + GimpBrushHeader bh; + TQCString utf8Name = name().utf8(); // Names in v2 brushes are in UTF-8 + char const* name = utf8Name.data(); + int nameLength = tqstrlen(name); + int wrote; + + bh.header_size = htonl(sizeof(GimpBrushHeader) + nameLength); + bh.version = htonl(2); // Only RGBA8 data needed atm, no cinepaint stuff + bh.width = htonl(width()); + bh.height = htonl(height()); + // Hardcoded, 4 bytes RGBA or 1 byte GREY + if (!hasColor()) + bh.bytes = htonl(1); + else + bh.bytes = htonl(4); + bh.magic_number = htonl(GimpV2BrushMagic); + bh.spacing = htonl(static_cast(spacing() * 100.0)); + + // Write header: first bh, then the name + TQByteArray bytes; + bytes.setRawData(reinterpret_cast(&bh), sizeof(GimpBrushHeader)); + wrote = dev->writeBlock(bytes); + bytes.resetRawData(reinterpret_cast(&bh), sizeof(GimpBrushHeader)); + + if (wrote == -1) + return false; + + wrote = dev->writeBlock(name, nameLength); // No +1 for the trailing NULL it seems... + if (wrote == -1) + return false; + + int k = 0; + + if (!hasColor()) { + bytes.resize(width() * height()); + for (TQ_INT32 y = 0; y < height(); y++) { + for (TQ_INT32 x = 0; x < width(); x++) { + TQRgb c = m_img.pixel(x, y); + bytes[k++] = static_cast(255 - tqRed(c)); // red == blue == green + } + } + } else { + bytes.resize(width() * height() * 4); + for (TQ_INT32 y = 0; y < height(); y++) { + for (TQ_INT32 x = 0; x < width(); x++) { + // order for gimp brushes, v2 is: RGBA + TQRgb pixel = m_img.pixel(x,y); + bytes[k++] = static_cast(tqRed(pixel)); + bytes[k++] = static_cast(tqGreen(pixel)); + bytes[k++] = static_cast(tqBlue(pixel)); + bytes[k++] = static_cast(tqAlpha(pixel)); + } + } + } + + wrote = dev->writeBlock(bytes); + if (wrote == -1) + return false; + + return true; +} + +TQImage KisBrush::img() +{ + TQImage image = m_img; + + if (hasColor() && useColorAsMask()) { + image.detach(); + + for (int x = 0; x < image.width(); x++) { + for (int y = 0; y < image.height(); y++) { + TQRgb c = image.pixel(x, y); + int a = (tqGray(c) * tqAlpha(c)) / 255; + image.setPixel(x, y, tqRgba(a, 0, a, a)); + } + } + } + + return image; +} + +KisAlphaMaskSP KisBrush::mask(const KisPaintInformation& info, double subPixelX, double subPixelY) const +{ + if (m_scaledBrushes.isEmpty()) { + createScaledBrushes(); + } + + double scale = scaleForPressure(info.pressure); + + const ScaledBrush *aboveBrush = 0; + const ScaledBrush *belowBrush = 0; + + findScaledBrushes(scale, &aboveBrush, &belowBrush); + Q_ASSERT(aboveBrush != 0); + + KisAlphaMaskSP outputMask = 0; + + if (belowBrush != 0) { + // We're in between two masks. Interpolate between them. + + KisAlphaMaskSP scaledAboveMask = scaleMask(aboveBrush, scale, subPixelX, subPixelY); + KisAlphaMaskSP scaledBelowMask = scaleMask(belowBrush, scale, subPixelX, subPixelY); + + double t = (scale - belowBrush->scale()) / (aboveBrush->scale() - belowBrush->scale()); + + outputMask = KisAlphaMask::interpolate(scaledBelowMask, scaledAboveMask, t); + } else { + if (fabs(scale - aboveBrush->scale()) < DBL_EPSILON) { + // Exact match. + outputMask = scaleMask(aboveBrush, scale, subPixelX, subPixelY); + } else { + // We are smaller than the smallest mask, which is always 1x1. + double s = scale / aboveBrush->scale(); + outputMask = scaleSinglePixelMask(s, aboveBrush->mask()->alphaAt(0, 0), subPixelX, subPixelY); + } + } + + return outputMask; +} + +KisPaintDeviceSP KisBrush::image(KisColorSpace * /*colorSpace*/, const KisPaintInformation& info, double subPixelX, double subPixelY) const +{ + if (m_scaledBrushes.isEmpty()) { + createScaledBrushes(); + } + + double scale = scaleForPressure(info.pressure); + + const ScaledBrush *aboveBrush = 0; + const ScaledBrush *belowBrush = 0; + + findScaledBrushes(scale, &aboveBrush, &belowBrush); + Q_ASSERT(aboveBrush != 0); + + TQImage outputImage; + + if (belowBrush != 0) { + // We're in between two brushes. Interpolate between them. + + TQImage scaledAboveImage = scaleImage(aboveBrush, scale, subPixelX, subPixelY); + TQImage scaledBelowImage = scaleImage(belowBrush, scale, subPixelX, subPixelY); + + double t = (scale - belowBrush->scale()) / (aboveBrush->scale() - belowBrush->scale()); + + outputImage = interpolate(scaledBelowImage, scaledAboveImage, t); + } else { + if (fabs(scale - aboveBrush->scale()) < DBL_EPSILON) { + // Exact match. + outputImage = scaleImage(aboveBrush, scale, subPixelX, subPixelY); + } else { + // We are smaller than the smallest brush, which is always 1x1. + double s = scale / aboveBrush->scale(); + outputImage = scaleSinglePixelImage(s, aboveBrush->image().pixel(0, 0), subPixelX, subPixelY); + } + } + + int outputWidth = outputImage.width(); + int outputHeight = outputImage.height(); + + KisPaintDevice *layer = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "brush"); + + TQ_CHECK_PTR(layer); + + for (int y = 0; y < outputHeight; y++) { + KisHLineIterator iter = layer->createHLineIterator( 0, y, outputWidth, true); + for (int x = 0; x < outputWidth; x++) { + TQ_UINT8 * p = iter.rawData(); + + TQRgb pixel = outputImage.pixel(x, y); + int red = tqRed(pixel); + int green = tqGreen(pixel); + int blue = tqBlue(pixel); + int alpha = tqAlpha(pixel); + + // Scaled images are in pre-multiplied alpha form so + // divide by alpha. + // channel order is BGRA + if (alpha != 0) { + p[2] = (red * 255) / alpha; + p[1] = (green * 255) / alpha; + p[0] = (blue * 255) / alpha; + p[3] = alpha; + } + + ++iter; + } + } + + return layer; +} + +void KisBrush::setHotSpot(KisPoint pt) +{ + double x = pt.x(); + double y = pt.y(); + + if (x < 0) + x = 0; + else if (x >= width()) + x = width() - 1; + + if (y < 0) + y = 0; + else if (y >= height()) + y = height() - 1; + + m_hotSpot = KisPoint(x, y); +} + +KisPoint KisBrush::hotSpot(const KisPaintInformation& info) const +{ + double scale = scaleForPressure(info.pressure); + double w = width() * scale; + double h = height() * scale; + + // The smallest brush we can produce is a single pixel. + if (w < 1) { + w = 1; + } + + if (h < 1) { + h = 1; + } + + // XXX: This should take m_hotSpot into account, though it + // isn't specified by gimp brushes so it would default to the centre + // anyway. + KisPoint p(w / 2, h / 2); + return p; +} + +enumBrushType KisBrush::brushType() const +{ + if (m_brushType == IMAGE && useColorAsMask()) { + return MASK; + } + else { + return m_brushType; + } +} + +bool KisBrush::hasColor() const +{ + return m_hasColor; +} + +void KisBrush::createScaledBrushes() const +{ + if (!m_scaledBrushes.isEmpty()) + m_scaledBrushes.clear(); + + // Construct a series of brushes where each one's dimensions are + // half the size of the previous one. + int width = m_img.width() * MAXIMUM_SCALE; + int height = m_img.height() * MAXIMUM_SCALE; + + TQImage scaledImage; + + while (true) { + + if (width >= m_img.width() && height >= m_img.height()) { + scaledImage = scaleImage(m_img, width, height); + } + else { + // Scale down the previous image once we're below 1:1. + scaledImage = scaleImage(scaledImage, width, height); + } + + KisAlphaMaskSP scaledMask = new KisAlphaMask(scaledImage, hasColor()); + TQ_CHECK_PTR(scaledMask); + + double xScale = static_cast(width) / m_img.width(); + double yScale = static_cast(height) / m_img.height(); + double scale = xScale; + + m_scaledBrushes.append(ScaledBrush(scaledMask, hasColor() ? scaledImage : TQImage(), scale, xScale, yScale)); + + if (width == 1 && height == 1) { + break; + } + + // Round up so that we never have to scale an image by less than 1/2. + width = (width + 1) / 2; + height = (height + 1) / 2; + + } + +} + +double KisBrush::xSpacing(double pressure) const +{ + return width() * scaleForPressure(pressure) * m_spacing; +} + +double KisBrush::ySpacing(double pressure) const +{ + return height() * scaleForPressure(pressure) * m_spacing; +} + +double KisBrush::scaleForPressure(double pressure) +{ + double scale = pressure / PRESSURE_DEFAULT; + + if (scale < 0) { + scale = 0; + } + + if (scale > MAXIMUM_SCALE) { + scale = MAXIMUM_SCALE; + } + + return scale; +} + +TQ_INT32 KisBrush::maskWidth(const KisPaintInformation& info) const +{ + // Add one for sub-pixel shift + return static_cast(ceil(width() * scaleForPressure(info.pressure)) + 1); +} + +TQ_INT32 KisBrush::maskHeight(const KisPaintInformation& info) const +{ + // Add one for sub-pixel shift + return static_cast(ceil(height() * scaleForPressure(info.pressure)) + 1); +} + +KisAlphaMaskSP KisBrush::scaleMask(const ScaledBrush *srcBrush, double scale, double subPixelX, double subPixelY) const +{ + // Add one pixel for sub-pixel shifting + int dstWidth = static_cast(ceil(scale * width())) + 1; + int dstHeight = static_cast(ceil(scale * height())) + 1; + + KisAlphaMaskSP dstMask = new KisAlphaMask(dstWidth, dstHeight); + TQ_CHECK_PTR(dstMask); + + KisAlphaMaskSP srcMask = srcBrush->mask(); + + // Compute scales to map the scaled brush onto the required scale. + double xScale = srcBrush->xScale() / scale; + double yScale = srcBrush->yScale() / scale; + + int srcWidth = srcMask->width(); + int srcHeight = srcMask->height(); + + for (int dstY = 0; dstY < dstHeight; dstY++) { + for (int dstX = 0; dstX < dstWidth; dstX++) { + + double srcX = (dstX - subPixelX + 0.5) * xScale; + double srcY = (dstY - subPixelY + 0.5) * yScale; + + srcX -= 0.5; + srcY -= 0.5; + + int leftX = static_cast(srcX); + + if (srcX < 0) { + leftX--; + } + + double xInterp = srcX - leftX; + + int topY = static_cast(srcY); + + if (srcY < 0) { + topY--; + } + + double yInterp = srcY - topY; + + TQ_UINT8 topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcMask->alphaAt(leftX, topY) : OPACITY_TRANSPARENT; + TQ_UINT8 bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcMask->alphaAt(leftX, topY + 1) : OPACITY_TRANSPARENT; + TQ_UINT8 topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcMask->alphaAt(leftX + 1, topY) : OPACITY_TRANSPARENT; + TQ_UINT8 bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcMask->alphaAt(leftX + 1, topY + 1) : OPACITY_TRANSPARENT; + + double a = 1 - xInterp; + double b = 1 - yInterp; + + // Bi-linear interpolation + int d = static_cast(a * b * topLeft + + a * (1 - b) * bottomLeft + + (1 - a) * b * topRight + + (1 - a) * (1 - b) * bottomRight + 0.5); + + if (d < OPACITY_TRANSPARENT) { + d = OPACITY_TRANSPARENT; + } + else + if (d > OPACITY_OPAQUE) { + d = OPACITY_OPAQUE; + } + + dstMask->setAlphaAt(dstX, dstY, static_cast(d)); + } + } + + return dstMask; +} + +TQImage KisBrush::scaleImage(const ScaledBrush *srcBrush, double scale, double subPixelX, double subPixelY) const +{ + // Add one pixel for sub-pixel shifting + int dstWidth = static_cast(ceil(scale * width())) + 1; + int dstHeight = static_cast(ceil(scale * height())) + 1; + + TQImage dstImage(dstWidth, dstHeight, 32); + dstImage.setAlphaBuffer(true); + + const TQImage srcImage = srcBrush->image(); + + // Compute scales to map the scaled brush onto the required scale. + double xScale = srcBrush->xScale() / scale; + double yScale = srcBrush->yScale() / scale; + + int srcWidth = srcImage.width(); + int srcHeight = srcImage.height(); + + for (int dstY = 0; dstY < dstHeight; dstY++) { + for (int dstX = 0; dstX < dstWidth; dstX++) { + + double srcX = (dstX - subPixelX + 0.5) * xScale; + double srcY = (dstY - subPixelY + 0.5) * yScale; + + srcX -= 0.5; + srcY -= 0.5; + + int leftX = static_cast(srcX); + + if (srcX < 0) { + leftX--; + } + + double xInterp = srcX - leftX; + + int topY = static_cast(srcY); + + if (srcY < 0) { + topY--; + } + + double yInterp = srcY - topY; + + TQRgb topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX, topY) : tqRgba(0, 0, 0, 0); + TQRgb bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX, topY + 1) : tqRgba(0, 0, 0, 0); + TQRgb topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX + 1, topY) : tqRgba(0, 0, 0, 0); + TQRgb bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX + 1, topY + 1) : tqRgba(0, 0, 0, 0); + + double a = 1 - xInterp; + double b = 1 - yInterp; + + // Bi-linear interpolation. Image is pre-multiplied by alpha. + int red = static_cast(a * b * tqRed(topLeft) + + a * (1 - b) * tqRed(bottomLeft) + + (1 - a) * b * tqRed(topRight) + + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5); + int green = static_cast(a * b * tqGreen(topLeft) + + a * (1 - b) * tqGreen(bottomLeft) + + (1 - a) * b * tqGreen(topRight) + + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5); + int blue = static_cast(a * b * tqBlue(topLeft) + + a * (1 - b) * tqBlue(bottomLeft) + + (1 - a) * b * tqBlue(topRight) + + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5); + int alpha = static_cast(a * b * tqAlpha(topLeft) + + a * (1 - b) * tqAlpha(bottomLeft) + + (1 - a) * b * tqAlpha(topRight) + + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5); + + if (red < 0) { + red = 0; + } + else + if (red > 255) { + red = 255; + } + + if (green < 0) { + green = 0; + } + else + if (green > 255) { + green = 255; + } + + if (blue < 0) { + blue = 0; + } + else + if (blue > 255) { + blue = 255; + } + + if (alpha < 0) { + alpha = 0; + } + else + if (alpha > 255) { + alpha = 255; + } + + dstImage.setPixel(dstX, dstY, tqRgba(red, green, blue, alpha)); + } + } + + return dstImage; +} + +TQImage KisBrush::scaleImage(const TQImage& srcImage, int width, int height) +{ + TQImage scaledImage; + //TQString filename; + + int srcWidth = srcImage.width(); + int srcHeight = srcImage.height(); + + double xScale = static_cast(srcWidth) / width; + double yScale = static_cast(srcHeight) / height; + + if (xScale > 2 + DBL_EPSILON || yScale > 2 + DBL_EPSILON || xScale < 1 - DBL_EPSILON || yScale < 1 - DBL_EPSILON) { + // smoothScale gives better results when scaling an image up + // or scaling it to less than half size. + scaledImage = srcImage.smoothScale(width, height); + + //filename = TQString("smoothScale_%1x%2.png").arg(width).arg(height); + } + else { + scaledImage.create(width, height, 32); + scaledImage.setAlphaBuffer(srcImage.hasAlphaBuffer()); + + for (int dstY = 0; dstY < height; dstY++) { + for (int dstX = 0; dstX < width; dstX++) { + + double srcX = (dstX + 0.5) * xScale; + double srcY = (dstY + 0.5) * yScale; + + srcX -= 0.5; + srcY -= 0.5; + + int leftX = static_cast(srcX); + + if (srcX < 0) { + leftX--; + } + + double xInterp = srcX - leftX; + + int topY = static_cast(srcY); + + if (srcY < 0) { + topY--; + } + + double yInterp = srcY - topY; + + TQRgb topLeft = (leftX >= 0 && leftX < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX, topY) : tqRgba(0, 0, 0, 0); + TQRgb bottomLeft = (leftX >= 0 && leftX < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX, topY + 1) : tqRgba(0, 0, 0, 0); + TQRgb topRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY >= 0 && topY < srcHeight) ? srcImage.pixel(leftX + 1, topY) : tqRgba(0, 0, 0, 0); + TQRgb bottomRight = (leftX + 1 >= 0 && leftX + 1 < srcWidth && topY + 1 >= 0 && topY + 1 < srcHeight) ? srcImage.pixel(leftX + 1, topY + 1) : tqRgba(0, 0, 0, 0); + + double a = 1 - xInterp; + double b = 1 - yInterp; + + int red; + int green; + int blue; + int alpha; + + if (srcImage.hasAlphaBuffer()) { + red = static_cast(a * b * tqRed(topLeft) * tqAlpha(topLeft) + + a * (1 - b) * tqRed(bottomLeft) * tqAlpha(bottomLeft) + + (1 - a) * b * tqRed(topRight) * tqAlpha(topRight) + + (1 - a) * (1 - b) * tqRed(bottomRight) * tqAlpha(bottomRight) + 0.5); + green = static_cast(a * b * tqGreen(topLeft) * tqAlpha(topLeft) + + a * (1 - b) * tqGreen(bottomLeft) * tqAlpha(bottomLeft) + + (1 - a) * b * tqGreen(topRight) * tqAlpha(topRight) + + (1 - a) * (1 - b) * tqGreen(bottomRight) * tqAlpha(bottomRight) + 0.5); + blue = static_cast(a * b * tqBlue(topLeft) * tqAlpha(topLeft) + + a * (1 - b) * tqBlue(bottomLeft) * tqAlpha(bottomLeft) + + (1 - a) * b * tqBlue(topRight) * tqAlpha(topRight) + + (1 - a) * (1 - b) * tqBlue(bottomRight) * tqAlpha(bottomRight) + 0.5); + alpha = static_cast(a * b * tqAlpha(topLeft) + + a * (1 - b) * tqAlpha(bottomLeft) + + (1 - a) * b * tqAlpha(topRight) + + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5); + + if (alpha != 0) { + red /= alpha; + green /= alpha; + blue /= alpha; + } + } + else { + red = static_cast(a * b * tqRed(topLeft) + + a * (1 - b) * tqRed(bottomLeft) + + (1 - a) * b * tqRed(topRight) + + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5); + green = static_cast(a * b * tqGreen(topLeft) + + a * (1 - b) * tqGreen(bottomLeft) + + (1 - a) * b * tqGreen(topRight) + + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5); + blue = static_cast(a * b * tqBlue(topLeft) + + a * (1 - b) * tqBlue(bottomLeft) + + (1 - a) * b * tqBlue(topRight) + + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5); + alpha = 255; + } + + if (red < 0) { + red = 0; + } + else + if (red > 255) { + red = 255; + } + + if (green < 0) { + green = 0; + } + else + if (green > 255) { + green = 255; + } + + if (blue < 0) { + blue = 0; + } + else + if (blue > 255) { + blue = 255; + } + + if (alpha < 0) { + alpha = 0; + } + else + if (alpha > 255) { + alpha = 255; + } + + scaledImage.setPixel(dstX, dstY, tqRgba(red, green, blue, alpha)); + } + } + + //filename = TQString("bilinear_%1x%2.png").arg(width).arg(height); + } + + //scaledImage.save(filename, "PNG"); + + return scaledImage; +} + +void KisBrush::findScaledBrushes(double scale, const ScaledBrush **aboveBrush, const ScaledBrush **belowBrush) const +{ + uint current = 0; + + while (true) { + *aboveBrush = &(m_scaledBrushes[current]); + + if (fabs((*aboveBrush)->scale() - scale) < DBL_EPSILON) { + // Scale matches exactly + break; + } + + if (current == m_scaledBrushes.count() - 1) { + // This is the last one + break; + } + + if (scale > m_scaledBrushes[current + 1].scale() + DBL_EPSILON) { + // We fit in between the two. + *belowBrush = &(m_scaledBrushes[current + 1]); + break; + } + + current++; + } +} + +KisAlphaMaskSP KisBrush::scaleSinglePixelMask(double scale, TQ_UINT8 maskValue, double subPixelX, double subPixelY) +{ + int srcWidth = 1; + int srcHeight = 1; + int dstWidth = 2; + int dstHeight = 2; + KisAlphaMaskSP outputMask = new KisAlphaMask(dstWidth, dstHeight); + TQ_CHECK_PTR(outputMask); + + double a = subPixelX; + double b = subPixelY; + + for (int y = 0; y < dstHeight; y++) { + for (int x = 0; x < dstWidth; x++) { + + TQ_UINT8 topLeft = (x > 0 && y > 0) ? maskValue : OPACITY_TRANSPARENT; + TQ_UINT8 bottomLeft = (x > 0 && y < srcHeight) ? maskValue : OPACITY_TRANSPARENT; + TQ_UINT8 topRight = (x < srcWidth && y > 0) ? maskValue : OPACITY_TRANSPARENT; + TQ_UINT8 bottomRight = (x < srcWidth && y < srcHeight) ? maskValue : OPACITY_TRANSPARENT; + + // Bi-linear interpolation + int d = static_cast(a * b * topLeft + + a * (1 - b) * bottomLeft + + (1 - a) * b * topRight + + (1 - a) * (1 - b) * bottomRight + 0.5); + + // Multiply by the square of the scale because a 0.5x0.5 pixel + // has 0.25 the value of the 1x1. + d = static_cast(d * scale * scale + 0.5); + + if (d < OPACITY_TRANSPARENT) { + d = OPACITY_TRANSPARENT; + } + else + if (d > OPACITY_OPAQUE) { + d = OPACITY_OPAQUE; + } + + outputMask->setAlphaAt(x, y, static_cast(d)); + } + } + + return outputMask; +} + +TQImage KisBrush::scaleSinglePixelImage(double scale, TQRgb pixel, double subPixelX, double subPixelY) +{ + int srcWidth = 1; + int srcHeight = 1; + int dstWidth = 2; + int dstHeight = 2; + + TQImage outputImage(dstWidth, dstHeight, 32); + outputImage.setAlphaBuffer(true); + + double a = subPixelX; + double b = subPixelY; + + for (int y = 0; y < dstHeight; y++) { + for (int x = 0; x < dstWidth; x++) { + + TQRgb topLeft = (x > 0 && y > 0) ? pixel : tqRgba(0, 0, 0, 0); + TQRgb bottomLeft = (x > 0 && y < srcHeight) ? pixel : tqRgba(0, 0, 0, 0); + TQRgb topRight = (x < srcWidth && y > 0) ? pixel : tqRgba(0, 0, 0, 0); + TQRgb bottomRight = (x < srcWidth && y < srcHeight) ? pixel : tqRgba(0, 0, 0, 0); + + // Bi-linear interpolation. Images are in pre-multiplied form. + int red = static_cast(a * b * tqRed(topLeft) + + a * (1 - b) * tqRed(bottomLeft) + + (1 - a) * b * tqRed(topRight) + + (1 - a) * (1 - b) * tqRed(bottomRight) + 0.5); + int green = static_cast(a * b * tqGreen(topLeft) + + a * (1 - b) * tqGreen(bottomLeft) + + (1 - a) * b * tqGreen(topRight) + + (1 - a) * (1 - b) * tqGreen(bottomRight) + 0.5); + int blue = static_cast(a * b * tqBlue(topLeft) + + a * (1 - b) * tqBlue(bottomLeft) + + (1 - a) * b * tqBlue(topRight) + + (1 - a) * (1 - b) * tqBlue(bottomRight) + 0.5); + int alpha = static_cast(a * b * tqAlpha(topLeft) + + a * (1 - b) * tqAlpha(bottomLeft) + + (1 - a) * b * tqAlpha(topRight) + + (1 - a) * (1 - b) * tqAlpha(bottomRight) + 0.5); + + // Multiply by the square of the scale because a 0.5x0.5 pixel + // has 0.25 the value of the 1x1. + alpha = static_cast(alpha * scale * scale + 0.5); + + // Apply to the colour channels too since we are + // storing pre-multiplied by alpha. + red = static_cast(red * scale * scale + 0.5); + green = static_cast(green * scale * scale + 0.5); + blue = static_cast(blue * scale * scale + 0.5); + + if (red < 0) { + red = 0; + } + else + if (red > 255) { + red = 255; + } + + if (green < 0) { + green = 0; + } + else + if (green > 255) { + green = 255; + } + + if (blue < 0) { + blue = 0; + } + else + if (blue > 255) { + blue = 255; + } + + if (alpha < 0) { + alpha = 0; + } + else + if (alpha > 255) { + alpha = 255; + } + + outputImage.setPixel(x, y, tqRgba(red, green, blue, alpha)); + } + } + + return outputImage; +} + +TQImage KisBrush::interpolate(const TQImage& image1, const TQImage& image2, double t) +{ + Q_ASSERT((image1.width() == image2.width()) && (image1.height() == image2.height())); + Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); + + int width = image1.width(); + int height = image1.height(); + + TQImage outputImage(width, height, 32); + outputImage.setAlphaBuffer(true); + + for (int x = 0; x < width; x++) { + for (int y = 0; y < height; y++) { + TQRgb image1pixel = image1.pixel(x, y); + TQRgb image2pixel = image2.pixel(x, y); + + // Images are in pre-multiplied alpha format. + int red = static_cast((1 - t) * tqRed(image1pixel) + t * tqRed(image2pixel) + 0.5); + int green = static_cast((1 - t) * tqGreen(image1pixel) + t * tqGreen(image2pixel) + 0.5); + int blue = static_cast((1 - t) * tqBlue(image1pixel) + t * tqBlue(image2pixel) + 0.5); + int alpha = static_cast((1 - t) * tqAlpha(image1pixel) + t * tqAlpha(image2pixel) + 0.5); + + if (red < 0) { + red = 0; + } + else + if (red > 255) { + red = 255; + } + + if (green < 0) { + green = 0; + } + else + if (green > 255) { + green = 255; + } + + if (blue < 0) { + blue = 0; + } + else + if (blue > 255) { + blue = 255; + } + + if (alpha < 0) { + alpha = 0; + } + else + if (alpha > 255) { + alpha = 255; + } + + outputImage.setPixel(x, y, tqRgba(red, green, blue, alpha)); + } + } + + return outputImage; +} + +KisBrush::ScaledBrush::ScaledBrush() +{ + m_mask = 0; + m_image = TQImage(); + m_scale = 1; + m_xScale = 1; + m_yScale = 1; +} + +KisBrush::ScaledBrush::ScaledBrush(KisAlphaMaskSP scaledMask, const TQImage& scaledImage, double scale, double xScale, double yScale) +{ + m_mask = scaledMask; + m_image = scaledImage; + m_scale = scale; + m_xScale = xScale; + m_yScale = yScale; + + if (!m_image.isNull()) { + // Convert image to pre-multiplied by alpha. + + m_image.detach(); + + for (int y = 0; y < m_image.height(); y++) { + for (int x = 0; x < m_image.width(); x++) { + + TQRgb pixel = m_image.pixel(x, y); + + int red = tqRed(pixel); + int green = tqGreen(pixel); + int blue = tqBlue(pixel); + int alpha = tqAlpha(pixel); + + red = (red * alpha) / 255; + green = (green * alpha) / 255; + blue = (blue * alpha) / 255; + + m_image.setPixel(x, y, tqRgba(red, green, blue, alpha)); + } + } + } +} + +void KisBrush::setImage(const TQImage& img) +{ + m_img = img; + m_img.detach(); + + setWidth(img.width()); + setHeight(img.height()); + + m_scaledBrushes.clear(); + + setValid(true); +} + +TQ_INT32 KisBrush::width() const +{ + return m_width; +} + +void KisBrush::setWidth(TQ_INT32 w) +{ + m_width = w; +} + +TQ_INT32 KisBrush::height() const +{ + return m_height; +} + +void KisBrush::setHeight(TQ_INT32 h) +{ + m_height = h; +} + +/*TQImage KisBrush::outline(double pressure) { + KisLayerSP layer = image(KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""), + KisPaintInformation(pressure)); + KisBoundary bounds(layer.data()); + int w = maskWidth(pressure); + int h = maskHeight(pressure); + + bounds.generateBoundary(w, h); + TQPixmap pix(bounds.pixmap(w, h)); + TQImage result; + result = pix; + return result; +}*/ + +void KisBrush::generateBoundary() { + KisPaintDeviceSP dev; + int w = maskWidth(KisPaintInformation()); + int h = maskHeight(KisPaintInformation()); + + if (brushType() == IMAGE || brushType() == PIPE_IMAGE) { + dev = image(KisMetaRegistry::instance()->csRegistry() ->getColorSpace(KisID("RGBA",""),""), KisPaintInformation()); + } else { + KisAlphaMaskSP amask = mask(KisPaintInformation()); + KisColorSpace* cs = KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""); + dev = new KisPaintDevice(cs, "tmp for generateBoundary"); + for (int y = 0; y < h; y++) { + KisHLineIteratorPixel it = dev->createHLineIterator(0, y, w, true); + int x = 0; + + while(!it.isDone()) { + cs->setAlpha(it.rawData(), amask->alphaAt(x++, y), 1); + ++it; + } + } + } + + m_boundary = new KisBoundary(dev); + m_boundary->generateBoundary(w, h); +} + +KisBoundary KisBrush::boundary() { + if (!m_boundary) + generateBoundary(); + return *m_boundary; +} + +void KisBrush::makeMaskImage() { + if (!hasColor()) + return; + + TQImage img; + img.create(width(), height(), 32); + + if (m_img.width() == img.width() && m_img.height() == img.height()) { + for (int x = 0; x < width(); x++) { + for (int y = 0; y < height(); y++) { + TQRgb c = m_img.pixel(x, y); + int a = (tqGray(c) * tqAlpha(c)) / 255; // tqGray(black) = 0 + img.setPixel(x, y, tqRgba(a, a, a, 255)); + } + } + + m_img = img; + } + + m_brushType = MASK; + m_hasColor = false; + m_useColorAsMask = false; + delete m_boundary; + m_boundary = 0; + m_scaledBrushes.clear(); +} + +KisBrush* KisBrush::clone() const { + KisBrush* c = new KisBrush(""); + c->m_spacing = m_spacing; + c->m_useColorAsMask = m_useColorAsMask; + c->m_hasColor = m_useColorAsMask; + c->m_img = m_img; + c->m_width = m_width; + c->m_height = m_height; + c->m_ownData = false; + c->m_hotSpot = m_hotSpot; + c->m_brushType = m_brushType; + c->setValid(true); + + return c; +} + +#include "kis_brush.moc" + diff --git a/chalk/core/kis_command.cc b/chalk/core/kis_command.cc deleted file mode 100644 index 11da634b..00000000 --- a/chalk/core/kis_command.cc +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 1999 Michael Koch - * Copyright (c) 2002 Patrick Julien - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include "kis_command.h" - -KisCommand::KisCommand(KisUndoAdapter *adapter) -{ - m_name = ""; - m_undoAdapter = adapter; -} - -KisCommand::KisCommand(const TQString& name, KisUndoAdapter *adapter) -{ - m_name = name; - m_undoAdapter = adapter; -} - -KisCommand::~KisCommand() -{ -} - -TQString KisCommand::name() const -{ - return m_name; -} - diff --git a/chalk/core/kis_command.cpp b/chalk/core/kis_command.cpp new file mode 100644 index 00000000..11da634b --- /dev/null +++ b/chalk/core/kis_command.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1999 Michael Koch + * Copyright (c) 2002 Patrick Julien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include "kis_command.h" + +KisCommand::KisCommand(KisUndoAdapter *adapter) +{ + m_name = ""; + m_undoAdapter = adapter; +} + +KisCommand::KisCommand(const TQString& name, KisUndoAdapter *adapter) +{ + m_name = name; + m_undoAdapter = adapter; +} + +KisCommand::~KisCommand() +{ +} + +TQString KisCommand::name() const +{ + return m_name; +} + diff --git a/chalk/core/kis_convolution_painter.cc b/chalk/core/kis_convolution_painter.cc deleted file mode 100644 index f5ee5573..00000000 --- a/chalk/core/kis_convolution_painter.cc +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Copyright (c) 2005 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include - -#include "tqbrush.h" -#include "tqcolor.h" -#include "tqfontinfo.h" -#include "tqfontmetrics.h" -#include "tqpen.h" -#include "tqregion.h" -#include "tqwmatrix.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "kis_brush.h" -#include "kis_global.h" -#include "kis_image.h" -#include "kis_iterators_pixel.h" -#include "kis_layer.h" -#include "kis_paint_device.h" -#include "kis_painter.h" -#include "kis_pattern.h" -#include "kis_rect.h" -#include "kis_colorspace.h" -#include "kis_types.h" -#include "kis_vec.h" -#include "kis_selection.h" -#include "kis_convolution_painter.h" - - -KisKernelSP KisKernel::fromTQImage(const TQImage& img) -{ - KisKernelSP k = new KisKernel; - k->width = img.width(); - k->height = img.height(); - k->offset = 0; - uint count = k->width * k->height; - k->data = new TQ_INT32[count]; - TQ_INT32* itData = k->data; - TQ_UINT8* itImg = (TQ_UINT8*)img.bits(); - k->factor = 0; - for(uint i = 0; i < count; ++i , ++itData, itImg+=4) - { - *itData = 255 - ( *itImg + *(itImg+1) + *(itImg+2) ) / 3; - k->factor += *itData; - } - return k; -} - - -KisConvolutionPainter::KisConvolutionPainter() - : super() -{ -} - -KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device) : super(device) -{ -} - -void KisConvolutionPainter::applyMatrix(KisKernelSP kernel, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, - KisConvolutionBorderOp borderOp, - KisChannelInfo::enumChannelFlags channelFlags ) -{ - // Make the area we cover as small as possible - if (m_device->hasSelection()) { - - TQRect r = m_device->selection()->selectedRect().intersect(TQRect(x, y, w, h)); - x = r.x(); - y = r.y(); - w = r.width(); - h = r.height(); - - } - - if ( w == 0 && h == 0 ) return; - - // Determine the kernel's extent from the center pixel - TQ_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh; - kw = kernel->width; - kh = kernel->height; - khalfWidth = (kw - 1) / 2; - khalfHeight = (kh - 1) / 2; - - xLastMinuskhw = x + (w - khalfWidth); - yLastMinuskhh = y + (h - khalfHeight); - - // Don't try to convolve on an area smaller than the kernel, or with a kernel that is not square or has no center pixel. - if (w < kw || h < kh || (kw&1) == 0 || (kh&1) == 0 || kernel->factor == 0 ) return; - - m_cancelRequested = false; - int lastProgressPercent = 0; - emit notifyProgress(0); - - KisColorSpace * cs = m_device->colorSpace(); - - // Determine whether we convolve border pixels, or not. - switch (borderOp) { - case BORDER_DEFAULT_FILL : - break; - case BORDER_REPEAT: - applyMatrixRepeat(kernel, x, y, w, h, channelFlags); - return; - case BORDER_WRAP: - case BORDER_AVOID: - default : - x += khalfWidth; - y += khalfHeight; - w -= kw - 1; - h -= kh - 1; - } - - // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy. - - int cacheSize = kw * kh; - int cdepth = cs -> pixelSize(); - TQ_UINT8** pixelPtrCache = new TQ_UINT8*[cacheSize]; - for (int i = 0; i < cacheSize; i++) - pixelPtrCache[i] = new TQ_UINT8[cdepth]; -// pixelPtrCache.fill(0); - - // row == the y position of the pixel we want to change in the paint device - int row = y; - - for (; row < y + h; ++row) { - - // col = the x position of the pixel we want to change - int col = x; - - KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true); - bool needFull = true; - while (!hit.isDone()) { - - // Iterate over all contributing pixels that are covered by the kernel - // krow = the y position in the kernel matrix - if(needFull) - { - TQ_INT32 i = 0; - for (TQ_INT32 krow = 0; krow < kh; ++krow) { - - // col - khalfWidth = the left starting point of the kernel as centered on our pixel - // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel - // kw = the width of the kernel - - // Fill the cache with pointers to the pixels under the kernel - KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, (row - khalfHeight) + krow, kw, false); - while (!kit.isDone()) { - memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); - ++kit; - ++i; - } - } - needFull = false; - Q_ASSERT (i==kw*kh); - } else { - for (TQ_INT32 krow = 0; krow < kh; ++krow) { // shift the cache to the left - TQ_UINT8** d = pixelPtrCache + krow * kw; - //memmove( d, d + 1, (kw-1)*sizeof(TQ_UINT8*)); - for (int i = 0; i < (kw-1); i++) { - memcpy(d[i], d[i+1], cdepth); - } - } - TQ_INT32 i = kw - 1; - KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, row - khalfHeight, kh, false); - while (!kit.isDone()) { - memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); - ++kit; - i += kw; - } - } - if (hit.isSelected()) { - cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh); -// pixelPtrCache.fill(0); - } - ++col; - ++hit; - } - - int progressPercent = 100 - ((((y + h) - row) * 100) / h); - - if (progressPercent > lastProgressPercent) { - emit notifyProgress(progressPercent); - lastProgressPercent = progressPercent; - - if (m_cancelRequested) { - for (int i = 0; i < cacheSize; i++) - delete[] pixelPtrCache[i]; - delete[] pixelPtrCache; - - return; - } - } - - } - - addDirtyRect(TQRect(x, y, w, h)); - - emit notifyProgressDone(); - - for (int i = 0; i < cacheSize; i++) - delete[] pixelPtrCache[i]; - delete[] pixelPtrCache; -} - -void KisConvolutionPainter::applyMatrixRepeat(KisKernelSP kernel, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, - KisChannelInfo::enumChannelFlags channelFlags) -{ - int lastProgressPercent = 0; - // Determine the kernel's extent from the center pixel - TQ_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh; - kw = kernel->width; - kh = kernel->height; - khalfWidth = (kw - 1) / 2; - khalfHeight = (kh - 1) / 2; - - xLastMinuskhw = x + (w - khalfWidth); - yLastMinuskhh = y + (h - khalfHeight); - - KisColorSpace * cs = m_device->colorSpace(); - - // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy. - - int cacheSize = kw * kh; - int cdepth = cs -> pixelSize(); - TQ_UINT8** pixelPtrCache = new TQ_UINT8*[cacheSize]; - for (int i = 0; i < cacheSize; i++) - pixelPtrCache[i] = new TQ_UINT8[cdepth]; - - // row == the y position of the pixel we want to change in the paint device - int row = y; - - for (; row < y + h; ++row) { - - // col = the x position of the pixel we want to change - int col = x; - - KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true); - bool needFull = true; - - TQ_INT32 itStart = row - khalfHeight; - TQ_INT32 itH = kh; - if(itStart < 0) - { - itH += itStart; - itStart = 0; - } else if(itStart + kh > yLastMinuskhh) - { - itH -= itStart + kh - yLastMinuskhh; - } - KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false); - while (!hit.isDone()) { - - // Iterate over all contributing pixels that are covered by the kernel - // krow = the y position in the kernel matrix - if(needFull) // The cache has not been fill, so we need to fill it - { - TQ_INT32 i = 0; - TQ_INT32 krow = 0; - if( row < khalfHeight ) - { - // We are just outside the layer, all the row in the cache will be identical - // so we need to create them only once, and then to copy them - if( x < khalfWidth) - { // the left pixels are outside of the layer, in the corner - TQ_INT32 kcol = 0; - KisHLineIteratorPixel kit = m_device->createHLineIterator(0, 0, kw, false); - for(; kcol < (khalfWidth - x) + 1; ++kcol) - { // First copy the address of the topleft pixel - memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); - } - for(; kcol < kw; ++kcol) - { // Then copy the address of the rest of the line - ++kit; - memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); - } - } else { - uint kcol = 0; - KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, 0, kw, false); - while (!kit.isDone()) { - memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); - ++kit; - ++kcol; - } - } - krow = 1; // we have allready done the first krow - for(;krow < (khalfHeight - row); ++krow) - { - // Copy the first line in the current line - for (int i = 0; i < kw; i++) - memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[i], cdepth); - } - i = krow * kw; - } - TQ_INT32 itH = kh; - if(row + khalfHeight > yLastMinuskhh) - { - itH += yLastMinuskhh - row - khalfHeight; - } - for (; krow < itH; ++krow) { - - // col - khalfWidth = the left starting point of the kernel as centered on our pixel - // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel - // kw = the width of the kernel - - // Fill the cache with pointers to the pixels under the kernel - TQ_INT32 itHStart = col - khalfWidth; - TQ_INT32 itW = kw; - if(itHStart < 0) - { - itW += itHStart; - itHStart = 0; - } - KisHLineIteratorPixel kit = m_device->createHLineIterator(itHStart, (row - khalfHeight) + krow, itW, false); - if( col < khalfWidth ) - { - for(; i < krow * kw + ( kw - itW ); i+= 1) - { - memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); - } - } - while (!kit.isDone()) { - memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); - ++kit; - ++i; - } - } - TQ_INT32 lastvalid = i - kw; - for(; krow < kh; ++krow) { - // Copy the last valid line in the current line - for (int i = 0; i < kw; i++) - memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[lastvalid + i], - cdepth); - } - needFull = false; - } else { -/* for (TQ_INT32 krow = 0; krow < kh; ++krow) { // shift the cache to the left - TQ_UINT8** d = pixelPtrCache + krow * kw; -// memmove( d, d + 1, (kw-1)*sizeof(TQ_UINT8*)); - for (int i = 0; i < (kw-1); i++) { - memcpy(d[i], d[i+1], cdepth); - } - }*/ - TQ_UINT8* firstincache = pixelPtrCache[0]; - memmove(pixelPtrCache, pixelPtrCache + 1, (cacheSize - 1) * sizeof(TQ_UINT8*) ); - pixelPtrCache[cacheSize - 1] = firstincache; - if(col < xLastMinuskhw) - { - TQ_INT32 i = kw - 1; -// KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false); - kit.nextCol(); - if( row < khalfHeight ) - { - for(; i < (khalfHeight- row ) * kw; i+=kw) - { - memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); - } - } - while (!kit.isDone()) { - memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); - ++kit; - i += kw; - } - TQ_INT32 lastvalid = i - kw; - for(;i < kw*kh; i+=kw) - { - memcpy(pixelPtrCache[i], pixelPtrCache[lastvalid], cdepth); - } - } - } - if (hit.isSelected()) { - cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh); - } - ++col; - ++hit; - } - - int progressPercent = 100 - ((((y + h) - row) * 100) / h); - - if (progressPercent > lastProgressPercent) { - emit notifyProgress(progressPercent); - lastProgressPercent = progressPercent; - - if (m_cancelRequested) { - for (int i = 0; i < cacheSize; i++) - delete[] pixelPtrCache[i]; - delete[] pixelPtrCache; - return; - } - } - - } - - addDirtyRect(TQRect(x, y, w, h)); - - emit notifyProgressDone(); - for (int i = 0; i < cacheSize; i++) - delete[] pixelPtrCache[i]; - delete[] pixelPtrCache; -} diff --git a/chalk/core/kis_convolution_painter.cpp b/chalk/core/kis_convolution_painter.cpp new file mode 100644 index 00000000..f5ee5573 --- /dev/null +++ b/chalk/core/kis_convolution_painter.cpp @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2005 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "tqbrush.h" +#include "tqcolor.h" +#include "tqfontinfo.h" +#include "tqfontmetrics.h" +#include "tqpen.h" +#include "tqregion.h" +#include "tqwmatrix.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "kis_brush.h" +#include "kis_global.h" +#include "kis_image.h" +#include "kis_iterators_pixel.h" +#include "kis_layer.h" +#include "kis_paint_device.h" +#include "kis_painter.h" +#include "kis_pattern.h" +#include "kis_rect.h" +#include "kis_colorspace.h" +#include "kis_types.h" +#include "kis_vec.h" +#include "kis_selection.h" +#include "kis_convolution_painter.h" + + +KisKernelSP KisKernel::fromTQImage(const TQImage& img) +{ + KisKernelSP k = new KisKernel; + k->width = img.width(); + k->height = img.height(); + k->offset = 0; + uint count = k->width * k->height; + k->data = new TQ_INT32[count]; + TQ_INT32* itData = k->data; + TQ_UINT8* itImg = (TQ_UINT8*)img.bits(); + k->factor = 0; + for(uint i = 0; i < count; ++i , ++itData, itImg+=4) + { + *itData = 255 - ( *itImg + *(itImg+1) + *(itImg+2) ) / 3; + k->factor += *itData; + } + return k; +} + + +KisConvolutionPainter::KisConvolutionPainter() + : super() +{ +} + +KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device) : super(device) +{ +} + +void KisConvolutionPainter::applyMatrix(KisKernelSP kernel, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, + KisConvolutionBorderOp borderOp, + KisChannelInfo::enumChannelFlags channelFlags ) +{ + // Make the area we cover as small as possible + if (m_device->hasSelection()) { + + TQRect r = m_device->selection()->selectedRect().intersect(TQRect(x, y, w, h)); + x = r.x(); + y = r.y(); + w = r.width(); + h = r.height(); + + } + + if ( w == 0 && h == 0 ) return; + + // Determine the kernel's extent from the center pixel + TQ_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh; + kw = kernel->width; + kh = kernel->height; + khalfWidth = (kw - 1) / 2; + khalfHeight = (kh - 1) / 2; + + xLastMinuskhw = x + (w - khalfWidth); + yLastMinuskhh = y + (h - khalfHeight); + + // Don't try to convolve on an area smaller than the kernel, or with a kernel that is not square or has no center pixel. + if (w < kw || h < kh || (kw&1) == 0 || (kh&1) == 0 || kernel->factor == 0 ) return; + + m_cancelRequested = false; + int lastProgressPercent = 0; + emit notifyProgress(0); + + KisColorSpace * cs = m_device->colorSpace(); + + // Determine whether we convolve border pixels, or not. + switch (borderOp) { + case BORDER_DEFAULT_FILL : + break; + case BORDER_REPEAT: + applyMatrixRepeat(kernel, x, y, w, h, channelFlags); + return; + case BORDER_WRAP: + case BORDER_AVOID: + default : + x += khalfWidth; + y += khalfHeight; + w -= kw - 1; + h -= kh - 1; + } + + // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy. + + int cacheSize = kw * kh; + int cdepth = cs -> pixelSize(); + TQ_UINT8** pixelPtrCache = new TQ_UINT8*[cacheSize]; + for (int i = 0; i < cacheSize; i++) + pixelPtrCache[i] = new TQ_UINT8[cdepth]; +// pixelPtrCache.fill(0); + + // row == the y position of the pixel we want to change in the paint device + int row = y; + + for (; row < y + h; ++row) { + + // col = the x position of the pixel we want to change + int col = x; + + KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true); + bool needFull = true; + while (!hit.isDone()) { + + // Iterate over all contributing pixels that are covered by the kernel + // krow = the y position in the kernel matrix + if(needFull) + { + TQ_INT32 i = 0; + for (TQ_INT32 krow = 0; krow < kh; ++krow) { + + // col - khalfWidth = the left starting point of the kernel as centered on our pixel + // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel + // kw = the width of the kernel + + // Fill the cache with pointers to the pixels under the kernel + KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, (row - khalfHeight) + krow, kw, false); + while (!kit.isDone()) { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + ++kit; + ++i; + } + } + needFull = false; + Q_ASSERT (i==kw*kh); + } else { + for (TQ_INT32 krow = 0; krow < kh; ++krow) { // shift the cache to the left + TQ_UINT8** d = pixelPtrCache + krow * kw; + //memmove( d, d + 1, (kw-1)*sizeof(TQ_UINT8*)); + for (int i = 0; i < (kw-1); i++) { + memcpy(d[i], d[i+1], cdepth); + } + } + TQ_INT32 i = kw - 1; + KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, row - khalfHeight, kh, false); + while (!kit.isDone()) { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + ++kit; + i += kw; + } + } + if (hit.isSelected()) { + cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh); +// pixelPtrCache.fill(0); + } + ++col; + ++hit; + } + + int progressPercent = 100 - ((((y + h) - row) * 100) / h); + + if (progressPercent > lastProgressPercent) { + emit notifyProgress(progressPercent); + lastProgressPercent = progressPercent; + + if (m_cancelRequested) { + for (int i = 0; i < cacheSize; i++) + delete[] pixelPtrCache[i]; + delete[] pixelPtrCache; + + return; + } + } + + } + + addDirtyRect(TQRect(x, y, w, h)); + + emit notifyProgressDone(); + + for (int i = 0; i < cacheSize; i++) + delete[] pixelPtrCache[i]; + delete[] pixelPtrCache; +} + +void KisConvolutionPainter::applyMatrixRepeat(KisKernelSP kernel, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, + KisChannelInfo::enumChannelFlags channelFlags) +{ + int lastProgressPercent = 0; + // Determine the kernel's extent from the center pixel + TQ_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh; + kw = kernel->width; + kh = kernel->height; + khalfWidth = (kw - 1) / 2; + khalfHeight = (kh - 1) / 2; + + xLastMinuskhw = x + (w - khalfWidth); + yLastMinuskhh = y + (h - khalfHeight); + + KisColorSpace * cs = m_device->colorSpace(); + + // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy. + + int cacheSize = kw * kh; + int cdepth = cs -> pixelSize(); + TQ_UINT8** pixelPtrCache = new TQ_UINT8*[cacheSize]; + for (int i = 0; i < cacheSize; i++) + pixelPtrCache[i] = new TQ_UINT8[cdepth]; + + // row == the y position of the pixel we want to change in the paint device + int row = y; + + for (; row < y + h; ++row) { + + // col = the x position of the pixel we want to change + int col = x; + + KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true); + bool needFull = true; + + TQ_INT32 itStart = row - khalfHeight; + TQ_INT32 itH = kh; + if(itStart < 0) + { + itH += itStart; + itStart = 0; + } else if(itStart + kh > yLastMinuskhh) + { + itH -= itStart + kh - yLastMinuskhh; + } + KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false); + while (!hit.isDone()) { + + // Iterate over all contributing pixels that are covered by the kernel + // krow = the y position in the kernel matrix + if(needFull) // The cache has not been fill, so we need to fill it + { + TQ_INT32 i = 0; + TQ_INT32 krow = 0; + if( row < khalfHeight ) + { + // We are just outside the layer, all the row in the cache will be identical + // so we need to create them only once, and then to copy them + if( x < khalfWidth) + { // the left pixels are outside of the layer, in the corner + TQ_INT32 kcol = 0; + KisHLineIteratorPixel kit = m_device->createHLineIterator(0, 0, kw, false); + for(; kcol < (khalfWidth - x) + 1; ++kcol) + { // First copy the address of the topleft pixel + memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); + } + for(; kcol < kw; ++kcol) + { // Then copy the address of the rest of the line + ++kit; + memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); + } + } else { + uint kcol = 0; + KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, 0, kw, false); + while (!kit.isDone()) { + memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); + ++kit; + ++kcol; + } + } + krow = 1; // we have allready done the first krow + for(;krow < (khalfHeight - row); ++krow) + { + // Copy the first line in the current line + for (int i = 0; i < kw; i++) + memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[i], cdepth); + } + i = krow * kw; + } + TQ_INT32 itH = kh; + if(row + khalfHeight > yLastMinuskhh) + { + itH += yLastMinuskhh - row - khalfHeight; + } + for (; krow < itH; ++krow) { + + // col - khalfWidth = the left starting point of the kernel as centered on our pixel + // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel + // kw = the width of the kernel + + // Fill the cache with pointers to the pixels under the kernel + TQ_INT32 itHStart = col - khalfWidth; + TQ_INT32 itW = kw; + if(itHStart < 0) + { + itW += itHStart; + itHStart = 0; + } + KisHLineIteratorPixel kit = m_device->createHLineIterator(itHStart, (row - khalfHeight) + krow, itW, false); + if( col < khalfWidth ) + { + for(; i < krow * kw + ( kw - itW ); i+= 1) + { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + } + } + while (!kit.isDone()) { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + ++kit; + ++i; + } + } + TQ_INT32 lastvalid = i - kw; + for(; krow < kh; ++krow) { + // Copy the last valid line in the current line + for (int i = 0; i < kw; i++) + memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[lastvalid + i], + cdepth); + } + needFull = false; + } else { +/* for (TQ_INT32 krow = 0; krow < kh; ++krow) { // shift the cache to the left + TQ_UINT8** d = pixelPtrCache + krow * kw; +// memmove( d, d + 1, (kw-1)*sizeof(TQ_UINT8*)); + for (int i = 0; i < (kw-1); i++) { + memcpy(d[i], d[i+1], cdepth); + } + }*/ + TQ_UINT8* firstincache = pixelPtrCache[0]; + memmove(pixelPtrCache, pixelPtrCache + 1, (cacheSize - 1) * sizeof(TQ_UINT8*) ); + pixelPtrCache[cacheSize - 1] = firstincache; + if(col < xLastMinuskhw) + { + TQ_INT32 i = kw - 1; +// KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false); + kit.nextCol(); + if( row < khalfHeight ) + { + for(; i < (khalfHeight- row ) * kw; i+=kw) + { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + } + } + while (!kit.isDone()) { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + ++kit; + i += kw; + } + TQ_INT32 lastvalid = i - kw; + for(;i < kw*kh; i+=kw) + { + memcpy(pixelPtrCache[i], pixelPtrCache[lastvalid], cdepth); + } + } + } + if (hit.isSelected()) { + cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh); + } + ++col; + ++hit; + } + + int progressPercent = 100 - ((((y + h) - row) * 100) / h); + + if (progressPercent > lastProgressPercent) { + emit notifyProgress(progressPercent); + lastProgressPercent = progressPercent; + + if (m_cancelRequested) { + for (int i = 0; i < cacheSize; i++) + delete[] pixelPtrCache[i]; + delete[] pixelPtrCache; + return; + } + } + + } + + addDirtyRect(TQRect(x, y, w, h)); + + emit notifyProgressDone(); + for (int i = 0; i < cacheSize; i++) + delete[] pixelPtrCache[i]; + delete[] pixelPtrCache; +} diff --git a/chalk/core/kis_exif_info.cc b/chalk/core/kis_exif_info.cc deleted file mode 100644 index 5d54ed5e..00000000 --- a/chalk/core/kis_exif_info.cc +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is part of Chalk - * - * Copyright (c) 2006 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_exif_info.h" - -#include - -#include - -KisExifInfo::KisExifInfo() -{} - - -KisExifInfo::~KisExifInfo() -{} - - -bool KisExifInfo::load(const TQDomElement& elmt) -{ - if(elmt.tagName() != "ExifInfo") - return false; - for( TQDomNode node = elmt.firstChild(); !node.isNull(); node = node.nextSibling() ) - { - TQDomElement e = node.toElement(); - if ( !e.isNull() ) - { - if(e.tagName() == "ExifValue") - { - TQString key = e.attribute("name"); - ExifValue eV; - eV.load(e); - setValue(key, eV); - } - } - } - return true; -} - -TQDomElement KisExifInfo::save(TQDomDocument& doc) -{ - TQDomElement elmt = doc.createElement("ExifInfo"); - for( KisExifInfo::evMap::const_iterator it = begin(); it != end(); ++it) - { - ExifValue ev = it.data(); - TQDomElement evD = ev.save( doc); - evD.setAttribute("name", it.key()); - elmt.appendChild(evD); - } - return elmt; -} diff --git a/chalk/core/kis_exif_info.cpp b/chalk/core/kis_exif_info.cpp new file mode 100644 index 00000000..5d54ed5e --- /dev/null +++ b/chalk/core/kis_exif_info.cpp @@ -0,0 +1,66 @@ +/* + * This file is part of Chalk + * + * Copyright (c) 2006 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_exif_info.h" + +#include + +#include + +KisExifInfo::KisExifInfo() +{} + + +KisExifInfo::~KisExifInfo() +{} + + +bool KisExifInfo::load(const TQDomElement& elmt) +{ + if(elmt.tagName() != "ExifInfo") + return false; + for( TQDomNode node = elmt.firstChild(); !node.isNull(); node = node.nextSibling() ) + { + TQDomElement e = node.toElement(); + if ( !e.isNull() ) + { + if(e.tagName() == "ExifValue") + { + TQString key = e.attribute("name"); + ExifValue eV; + eV.load(e); + setValue(key, eV); + } + } + } + return true; +} + +TQDomElement KisExifInfo::save(TQDomDocument& doc) +{ + TQDomElement elmt = doc.createElement("ExifInfo"); + for( KisExifInfo::evMap::const_iterator it = begin(); it != end(); ++it) + { + ExifValue ev = it.data(); + TQDomElement evD = ev.save( doc); + evD.setAttribute("name", it.key()); + elmt.appendChild(evD); + } + return elmt; +} diff --git a/chalk/core/kis_exif_value.cc b/chalk/core/kis_exif_value.cc deleted file mode 100644 index 134d4009..00000000 --- a/chalk/core/kis_exif_value.cc +++ /dev/null @@ -1,689 +0,0 @@ -/* - * This file is part of Chalk - * - * Copyright (c) 2006 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "kis_exif_value.h" - -#include -#include - -namespace { -void set16Bit (unsigned char *data, ExifValue::ByteOrder order, const TQ_UINT16* value) -{ - switch (order) { - case ExifValue::BYTE_ORDER_MOTOROLA: - data[0] = (unsigned char) (*value >> 8); - data[1] = (unsigned char) *value; - break; - case ExifValue::BYTE_ORDER_INTEL: - data[0] = (unsigned char) *value; - data[1] = (unsigned char) (*value >> 8); - break; - } -} - -void get16Bit (const unsigned char *data, ExifValue::ByteOrder order, TQ_UINT16* value) -{ - switch (order) { - case ExifValue::BYTE_ORDER_MOTOROLA: - *value = ((data[0] << 8) | data[1]); - break; - case ExifValue::BYTE_ORDER_INTEL: - *value = ((data[1] << 8) | data[0]); - break; - } -} - -void get32Bit (const unsigned char *data, ExifValue::ByteOrder order, TQ_UINT32* value) -{ - switch (order) { - case ExifValue::BYTE_ORDER_MOTOROLA: - *value = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]); - break; - case ExifValue::BYTE_ORDER_INTEL: - *value = ((data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0]); - } -} - -void set32Bit(unsigned char *data, ExifValue::ByteOrder order, const TQ_UINT32* value) -{ - switch (order) { - case ExifValue::BYTE_ORDER_MOTOROLA: - data[0] = (unsigned char) (*value >> 24); - data[1] = (unsigned char) (*value >> 16); - data[2] = (unsigned char) (*value >> 8); - data[3] = (unsigned char) *value; - break; - case ExifValue::BYTE_ORDER_INTEL: - data[3] = (unsigned char) (*value >> 24); - data[2] = (unsigned char) (*value >> 16); - data[1] = (unsigned char) (*value >> 8); - data[0] = (unsigned char) *value; - break; - } -} - -void get64Bit (const unsigned char *data, ExifValue::ByteOrder order, TQ_UINT64* value) -{ - switch (order) { - case ExifValue::BYTE_ORDER_MOTOROLA: - *value = (((TQ_UINT64)data[0] << 56) | ((TQ_UINT64)data[1] << 48) | ((TQ_UINT64)data[2] << 40) | ((TQ_UINT64)data[3] << 32) | ((TQ_UINT64)data[4] << 24) | ((TQ_UINT64)data[5] << 16) | ((TQ_UINT64)data[6] << 8) | (TQ_UINT64)data[7]); - break; - case ExifValue::BYTE_ORDER_INTEL: - *value = (((TQ_UINT64)data[7] << 56) | ((TQ_UINT64)data[6] << 48) | ((TQ_UINT64)data[5] << 40) | ((TQ_UINT64)data[4] << 32) | ((TQ_UINT64)data[3] << 24) | ((TQ_UINT64)data[2] << 16) | ((TQ_UINT64)data[1] << 8) | (TQ_UINT64)data[0]); - } -} - -void set64Bit(unsigned char *data, ExifValue::ByteOrder order, const TQ_UINT64* value) -{ - switch (order) { - case ExifValue::BYTE_ORDER_MOTOROLA: - data[0] = (unsigned char) (*value >> 56); - data[1] = (unsigned char) (*value >> 48); - data[2] = (unsigned char) (*value >> 40); - data[3] = (unsigned char) (*value >> 32); - data[4] = (unsigned char) (*value >> 24); - data[5] = (unsigned char) (*value >> 16); - data[6] = (unsigned char) (*value >> 8); - data[7] = (unsigned char) *value; - break; - case ExifValue::BYTE_ORDER_INTEL: - data[7] = (unsigned char) (*value >> 56); - data[6] = (unsigned char) (*value >> 48); - data[5] = (unsigned char) (*value >> 40); - data[4] = (unsigned char) (*value >> 32); - data[3] = (unsigned char) (*value >> 24); - data[2] = (unsigned char) (*value >> 16); - data[1] = (unsigned char) (*value >> 8); - data[0] = (unsigned char) *value; - break; - } -} - - -} - -ExifValue::ExifValue(ExifType ntype, unsigned char *data, unsigned int size, int ifd, uint ncomponents, ExifValue::ByteOrder order ) : m_ifd(ifd), m_type(ntype), m_components(ncomponents), m_value(0) -{ - allocData(); - setValue(data, size, order); -} - -void ExifValue::allocData() -{ - if( type() != EXIF_TYPE_ASCII && type() != EXIF_TYPE_UNDEFINED) - { - m_value = new ExifNumber[components()]; - } else if ( type() == EXIF_TYPE_ASCII ) - { - m_value = new TQString(); - } else if ( type() == EXIF_TYPE_UNDEFINED) - { - m_value = new UByteArray(); - } -} - -bool ExifValue::load(const TQDomElement& elmt) -{ - TQString attr; - if( (attr = elmt.attribute("ifd")).isNull() ) - return false; - m_ifd = attr.toInt(); - if( (attr = elmt.attribute("components")).isNull() ) - return false; - m_components = attr.toInt(); - if( (attr = elmt.attribute("type")).isNull() ) - return false; - m_type = (ExifValue::ExifType)attr.toInt(); - allocData(); - switch(type()) - { - case EXIF_TYPE_BYTE: - for(uint i = 0; i < components(); i++) - { - if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) - { - setValue(i, (TQ_UINT8)0); - } else { - setValue(i, (TQ_UINT8) attr.toUInt()); - } - } - break; - case EXIF_TYPE_ASCII: - setAsAscii( elmt.attribute("value" ) ); - break; - case EXIF_TYPE_SHORT: - for(uint i = 0; i < components(); i++) - { - if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) - { - setValue(i, (TQ_UINT16)0); - } else { - setValue(i, (TQ_UINT16) attr.toUInt()); - } - } - break; - case EXIF_TYPE_LONG: - for(uint i = 0; i < components(); i++) - { - if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) - { - setValue(i, (TQ_UINT32)0); - } else { - setValue(i, (TQ_UINT32) attr.toUInt()); - } - } - break; - case EXIF_TYPE_RATIONAL: - for(uint i = 0; i < components(); i++) - { - KisExifRational r; - if( (attr = elmt.attribute(TQString("numerator%1").arg(i) ) ).isNull() ) - { - r.numerator = (TQ_UINT32)0; - } else { - r.numerator = (TQ_UINT32) attr.toUInt(); - } - if( (attr = elmt.attribute(TQString("denominator%1").arg(i) ) ).isNull() ) - { - r.denominator = (TQ_UINT32)0; - } else { - r.denominator = (TQ_UINT32) attr.toUInt(); - } - setValue(i, r); - } - break; - case EXIF_TYPE_SBYTE: - for(uint i = 0; i < components(); i++) - { - if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) - { - setValue(i, (TQ_INT8)0); - } else { - setValue(i, (TQ_INT8) attr.toInt()); - } - } - break; - case EXIF_TYPE_UNDEFINED: - { - TQString instr = elmt.attribute("value"); - TQByteArray out; - TQByteArray in = instr.utf8(); - KCodecs::base64Decode( in, out); - out.resize(out.size() - 2 ); - setAsUndefined((uchar*)out.data(), out.size() ); - } - break; - case EXIF_TYPE_SSHORT: - for(uint i = 0; i < components(); i++) - { - if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) - { - setValue(i, (TQ_INT16)0); - } else { - setValue(i, (TQ_INT16) attr.toInt()); - } - } - break; - case EXIF_TYPE_SLONG: - for(uint i = 0; i < components(); i++) - { - if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) - { - setValue(i, (TQ_INT32)0); - } else { - setValue(i, (TQ_INT32) attr.toInt()); - } - } - break; - case EXIF_TYPE_SRATIONAL: - for(uint i = 0; i < components(); i++) - { - KisExifSRational r; - if( (attr = elmt.attribute(TQString("numerator%1").arg(i) ) ).isNull() ) - { - r.numerator = (TQ_INT32)0; - } else { - r.numerator = (TQ_INT32) attr.toInt(); - } - if( (attr = elmt.attribute(TQString("denominator%1").arg(i) ) ).isNull() ) - { - r.denominator = (TQ_UINT32)0; - } else { - r.denominator = (TQ_UINT32) attr.toInt(); - } - setValue(i, r); - } - break; - case EXIF_TYPE_FLOAT: - for(uint i = 0; i < components(); i++) - { - if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) - { - setValue(i, (float)0); - } else { - setValue(i, (float) attr.toFloat()); - } - } - break; - case EXIF_TYPE_DOUBLE: - for(uint i = 0; i < components(); i++) - { - if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) - { - setValue(i, (double)0); - } else { - setValue(i, (double) attr.toDouble()); - } - } - break; - case EXIF_TYPE_UNKNOW: - break; - - } - return true; -} - -TQDomElement ExifValue::save(TQDomDocument& doc) -{ - TQDomElement elmt = doc.createElement("ExifValue"); - elmt.setAttribute("ifd", ifd()); - elmt.setAttribute("components", components() ); - elmt.setAttribute("type", type() ); - switch(type()) - { - case EXIF_TYPE_BYTE: - for(uint i = 0; i < components(); i++) - elmt.setAttribute(TQString("value%1").arg(i), asByte( i ) ); - break; - case EXIF_TYPE_ASCII: - elmt.setAttribute("value", asAscii() ); - break; - case EXIF_TYPE_SHORT: - for(uint i = 0; i < components(); i++) - elmt.setAttribute(TQString("value%1").arg(i), asShort( i ) ); - break; - case EXIF_TYPE_LONG: - for(uint i = 0; i < components(); i++) - elmt.setAttribute(TQString("value%1").arg(i), asLong( i ) ); - break; - case EXIF_TYPE_RATIONAL: - for(uint i = 0; i < components(); i++) - { - KisExifRational r = asRational(i); - elmt.setAttribute(TQString("numerator%1").arg(i), r.numerator ); - elmt.setAttribute(TQString("denominator%1").arg(i), r.denominator ); - } - break; - case EXIF_TYPE_SBYTE: - for(uint i = 0; i < components(); i++) - elmt.setAttribute(TQString("value%1").arg(i), asSByte( i ) ); - break; - case EXIF_TYPE_UNDEFINED: - { - UByteArray value = asUndefined(); - TQByteArray data; - data.setRawData((char*)value.data(), value.size()); - TQByteArray encodedData; - KCodecs::base64Encode( data, encodedData ); - data.resetRawData( (char*)value.data(), value.size()); - elmt.setAttribute("value", TQString(encodedData)); - } - break; - case EXIF_TYPE_SSHORT: - for(uint i = 0; i < components(); i++) - elmt.setAttribute(TQString("value%1").arg(i), asSShort( i ) ); - break; - case EXIF_TYPE_SLONG: - for(uint i = 0; i < components(); i++) - elmt.setAttribute(TQString("value%1").arg(i), asSLong( i ) ); - break; - case EXIF_TYPE_SRATIONAL: - for(uint i = 0; i < components(); i++) - { - KisExifSRational r = asSRational(i); - elmt.setAttribute(TQString("numerator%1").arg(i), r.numerator ); - elmt.setAttribute(TQString("denominator%1").arg(i), r.denominator ); - } - break; - case EXIF_TYPE_FLOAT: - for(uint i = 0; i < components(); i++) - elmt.setAttribute(TQString("value%1").arg(i), asFloat( i ) ); - break; - case EXIF_TYPE_DOUBLE: - for(uint i = 0; i < components(); i++) - elmt.setAttribute(TQString("value%1").arg(i), asDouble( i ) ); - break; - case EXIF_TYPE_UNKNOW: - break; - } - return elmt; -} - - -void ExifValue::setValue(const unsigned char *data, unsigned int size, ExifValue::ByteOrder order) -{ - switch(type()) - { - case EXIF_TYPE_BYTE: - if( size == components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - n.m_byte = data[i]; - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_ASCII: - setAsAscii((char*) data); - break; - case EXIF_TYPE_SHORT: - if( size == 2*components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - get16Bit( data + 2 * i, order, &n.m_short); - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_LONG: - if( size == 4*components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - get32Bit( data + 4 * i, order, &n.m_long); - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_RATIONAL: - if( size == 8*components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - get32Bit( data + 8 * i, order, &n.m_rational.numerator); - get32Bit( data + 8 * i + 4, order, &n.m_rational.denominator); - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_SBYTE: - if( size == components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - n.m_sbyte = ((TQ_INT8*)data)[i]; - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_UNDEFINED: - setAsUndefined(data, size); - break; - case EXIF_TYPE_SSHORT: - if( size == 2*components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - get16Bit( data + 2 * i, order, (TQ_UINT16*)&n.m_sshort); - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_SLONG: - if( size == 4*components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - get32Bit( data + 4 * i, order, (TQ_UINT32*)&n.m_slong); - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_SRATIONAL: - if( size == 8*components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - get32Bit( data + 8 * i, order, (TQ_UINT32*)&n.m_srational.numerator); - get32Bit( data + 8 * i + 4, order, (TQ_UINT32*)&n.m_srational.denominator); - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_FLOAT: - if( size == 4*components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - get32Bit( data + 4 * i, order, (TQ_UINT32*)&n.m_float); - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_DOUBLE: - if( size == 8*components() ) - { - ExifNumber n; - for(uint i = 0; i < components(); i++) - { - get64Bit( data + 8 * i, order, (TQ_UINT64*)&n.m_double); - setAsExifNumber( i, n); - } - } - break; - case EXIF_TYPE_UNKNOW: - break; - } -} - -void ExifValue::convertToData(unsigned char ** data, unsigned int* size, ExifValue::ByteOrder order) -{ - switch(type()) - { - case EXIF_TYPE_BYTE: - *size = components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - (*data)[i] = asExifNumber(i).m_byte; - } - return; - case EXIF_TYPE_ASCII: - { - TQString str = asAscii(); - *size = str.length(); - *data = new uchar[ *size ]; - uchar* ptr = *data; - memcpy(ptr, str.ascii(), (*size)*sizeof(uchar)); - } - return; - break; - case EXIF_TYPE_SHORT: - { - *size = 2*components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - TQ_UINT16 aenms = asExifNumber(i).m_short; - set16Bit( (*data) + 2 * i, order, &aenms); - } - return; - } - case EXIF_TYPE_LONG: - { - *size = 4*components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - TQ_UINT32 aenml = asExifNumber(i).m_long; - set32Bit( (*data) + 4 * i, order, &aenml); - } - return; - } - case EXIF_TYPE_RATIONAL: - *size = 8*components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - ExifNumber n = asExifNumber(i); - set32Bit( (*data) + 8 * i, order, &n.m_rational.numerator); - set32Bit( (*data) + 8 * i + 4, order, &n.m_rational.denominator); - } - return; - case EXIF_TYPE_SBYTE: - *size = components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - *(((TQ_INT8*)*data) + i) = asExifNumber(i).m_sbyte; - } - return; - case EXIF_TYPE_UNDEFINED: - { - UByteArray array = asUndefined(); - *size = array.size(); - *data = new uchar[*size]; - memcpy( *data, array.data(), (*size)*sizeof(unsigned char)); - } - return; - case EXIF_TYPE_SSHORT: - *size = 2*components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - TQ_INT16 aenmss = asExifNumber(i).m_sshort; - set16Bit( (*data) + 2 * i, order, (TQ_UINT16*)&aenmss); - } - return; - case EXIF_TYPE_SLONG: - *size = 4*components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - TQ_INT32 aenmsl = asExifNumber(i).m_slong; - set32Bit( (*data) + 4 * i, order, (TQ_UINT32*)&aenmsl); - } - return; - case EXIF_TYPE_SRATIONAL: - *size = 8*components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - ExifNumber n = asExifNumber(i); - TQ_INT32 aenmsr_numerator = asExifNumber(i).m_srational.numerator; - TQ_INT32 aenmsr_denominator = asExifNumber(i).m_srational.denominator; - set32Bit( (*data) + 4 * i, order, (TQ_UINT32*)&aenmsr_numerator); - set32Bit( (*data) + 4 * i + 4, order, (TQ_UINT32*)&aenmsr_denominator); - } - return; - case EXIF_TYPE_FLOAT: - *size = 4*components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - float aenmf = asExifNumber(i).m_float; - set32Bit( (*data) + 4 * i, order, (TQ_UINT32*)&aenmf); - } - return; - case EXIF_TYPE_DOUBLE: - *size = 8*components(); - *data = new uchar[*size]; - for(uint i = 0; i < components(); i++) - { - double aenmd = asExifNumber(i).m_double; - set64Bit( (*data) + 4 * i, order, (TQ_UINT64*)&aenmd); - } - return; - case EXIF_TYPE_UNKNOW: - break; - } -} - -TQString ExifValue::toString() -{ - switch(type()) - { - case EXIF_TYPE_ASCII: - return asAscii(); - case EXIF_TYPE_UNDEFINED: - { - TQString undefined = "undefined"; - UByteArray array = asUndefined(); - for(uint i = 0; i < components(); i++) - { - undefined += "\\" + TQString().setNum( array[i] ); - } - return undefined; - } - default: - { - TQString str = ""; - for(uint i = 0; i < components(); i++) - { - str += toString(i); - } - return str; - } - } -} - -TQString ExifValue::toString(uint i) -{ - switch(type()) - { - case EXIF_TYPE_BYTE: - return TQString("%1 ").arg( asExifNumber( i ).m_byte ); - case EXIF_TYPE_SHORT: - return TQString("%1 ").arg( asExifNumber( i ).m_short ); - case EXIF_TYPE_LONG: - return TQString("%1 ").arg( asExifNumber( i ).m_long ); - case EXIF_TYPE_RATIONAL: - return TQString("%1 / %2 ").arg( asExifNumber( i ).m_rational.numerator ).arg( asExifNumber( i ).m_rational.denominator ); - case EXIF_TYPE_SBYTE: { - /* workaround to compiler bug on Raspbian Wheezy */ - TQ_INT8 o_sbyte = asExifNumber( i ).m_sbyte; - return TQString("%1 ").arg( o_sbyte ); - } - case EXIF_TYPE_SSHORT: - return TQString("%1 ").arg( asExifNumber( i ).m_sshort ); - case EXIF_TYPE_SLONG: - return TQString("%1 ").arg( asExifNumber( i ).m_slong ); - case EXIF_TYPE_SRATIONAL: - return TQString("%1 / %2 ").arg( asExifNumber( i ).m_srational.numerator ).arg( asExifNumber( i ).m_srational.denominator ); - case EXIF_TYPE_FLOAT: - return TQString("%1 ").arg( asExifNumber( i ).m_float ); - case EXIF_TYPE_DOUBLE: - return TQString("%1 ").arg( asExifNumber( i ).m_double ); - default: - return "unknow "; - } -} - diff --git a/chalk/core/kis_exif_value.cpp b/chalk/core/kis_exif_value.cpp new file mode 100644 index 00000000..134d4009 --- /dev/null +++ b/chalk/core/kis_exif_value.cpp @@ -0,0 +1,689 @@ +/* + * This file is part of Chalk + * + * Copyright (c) 2006 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_exif_value.h" + +#include +#include + +namespace { +void set16Bit (unsigned char *data, ExifValue::ByteOrder order, const TQ_UINT16* value) +{ + switch (order) { + case ExifValue::BYTE_ORDER_MOTOROLA: + data[0] = (unsigned char) (*value >> 8); + data[1] = (unsigned char) *value; + break; + case ExifValue::BYTE_ORDER_INTEL: + data[0] = (unsigned char) *value; + data[1] = (unsigned char) (*value >> 8); + break; + } +} + +void get16Bit (const unsigned char *data, ExifValue::ByteOrder order, TQ_UINT16* value) +{ + switch (order) { + case ExifValue::BYTE_ORDER_MOTOROLA: + *value = ((data[0] << 8) | data[1]); + break; + case ExifValue::BYTE_ORDER_INTEL: + *value = ((data[1] << 8) | data[0]); + break; + } +} + +void get32Bit (const unsigned char *data, ExifValue::ByteOrder order, TQ_UINT32* value) +{ + switch (order) { + case ExifValue::BYTE_ORDER_MOTOROLA: + *value = ((data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]); + break; + case ExifValue::BYTE_ORDER_INTEL: + *value = ((data[3] << 24) | (data[2] << 16) | (data[1] << 8) | data[0]); + } +} + +void set32Bit(unsigned char *data, ExifValue::ByteOrder order, const TQ_UINT32* value) +{ + switch (order) { + case ExifValue::BYTE_ORDER_MOTOROLA: + data[0] = (unsigned char) (*value >> 24); + data[1] = (unsigned char) (*value >> 16); + data[2] = (unsigned char) (*value >> 8); + data[3] = (unsigned char) *value; + break; + case ExifValue::BYTE_ORDER_INTEL: + data[3] = (unsigned char) (*value >> 24); + data[2] = (unsigned char) (*value >> 16); + data[1] = (unsigned char) (*value >> 8); + data[0] = (unsigned char) *value; + break; + } +} + +void get64Bit (const unsigned char *data, ExifValue::ByteOrder order, TQ_UINT64* value) +{ + switch (order) { + case ExifValue::BYTE_ORDER_MOTOROLA: + *value = (((TQ_UINT64)data[0] << 56) | ((TQ_UINT64)data[1] << 48) | ((TQ_UINT64)data[2] << 40) | ((TQ_UINT64)data[3] << 32) | ((TQ_UINT64)data[4] << 24) | ((TQ_UINT64)data[5] << 16) | ((TQ_UINT64)data[6] << 8) | (TQ_UINT64)data[7]); + break; + case ExifValue::BYTE_ORDER_INTEL: + *value = (((TQ_UINT64)data[7] << 56) | ((TQ_UINT64)data[6] << 48) | ((TQ_UINT64)data[5] << 40) | ((TQ_UINT64)data[4] << 32) | ((TQ_UINT64)data[3] << 24) | ((TQ_UINT64)data[2] << 16) | ((TQ_UINT64)data[1] << 8) | (TQ_UINT64)data[0]); + } +} + +void set64Bit(unsigned char *data, ExifValue::ByteOrder order, const TQ_UINT64* value) +{ + switch (order) { + case ExifValue::BYTE_ORDER_MOTOROLA: + data[0] = (unsigned char) (*value >> 56); + data[1] = (unsigned char) (*value >> 48); + data[2] = (unsigned char) (*value >> 40); + data[3] = (unsigned char) (*value >> 32); + data[4] = (unsigned char) (*value >> 24); + data[5] = (unsigned char) (*value >> 16); + data[6] = (unsigned char) (*value >> 8); + data[7] = (unsigned char) *value; + break; + case ExifValue::BYTE_ORDER_INTEL: + data[7] = (unsigned char) (*value >> 56); + data[6] = (unsigned char) (*value >> 48); + data[5] = (unsigned char) (*value >> 40); + data[4] = (unsigned char) (*value >> 32); + data[3] = (unsigned char) (*value >> 24); + data[2] = (unsigned char) (*value >> 16); + data[1] = (unsigned char) (*value >> 8); + data[0] = (unsigned char) *value; + break; + } +} + + +} + +ExifValue::ExifValue(ExifType ntype, unsigned char *data, unsigned int size, int ifd, uint ncomponents, ExifValue::ByteOrder order ) : m_ifd(ifd), m_type(ntype), m_components(ncomponents), m_value(0) +{ + allocData(); + setValue(data, size, order); +} + +void ExifValue::allocData() +{ + if( type() != EXIF_TYPE_ASCII && type() != EXIF_TYPE_UNDEFINED) + { + m_value = new ExifNumber[components()]; + } else if ( type() == EXIF_TYPE_ASCII ) + { + m_value = new TQString(); + } else if ( type() == EXIF_TYPE_UNDEFINED) + { + m_value = new UByteArray(); + } +} + +bool ExifValue::load(const TQDomElement& elmt) +{ + TQString attr; + if( (attr = elmt.attribute("ifd")).isNull() ) + return false; + m_ifd = attr.toInt(); + if( (attr = elmt.attribute("components")).isNull() ) + return false; + m_components = attr.toInt(); + if( (attr = elmt.attribute("type")).isNull() ) + return false; + m_type = (ExifValue::ExifType)attr.toInt(); + allocData(); + switch(type()) + { + case EXIF_TYPE_BYTE: + for(uint i = 0; i < components(); i++) + { + if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) + { + setValue(i, (TQ_UINT8)0); + } else { + setValue(i, (TQ_UINT8) attr.toUInt()); + } + } + break; + case EXIF_TYPE_ASCII: + setAsAscii( elmt.attribute("value" ) ); + break; + case EXIF_TYPE_SHORT: + for(uint i = 0; i < components(); i++) + { + if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) + { + setValue(i, (TQ_UINT16)0); + } else { + setValue(i, (TQ_UINT16) attr.toUInt()); + } + } + break; + case EXIF_TYPE_LONG: + for(uint i = 0; i < components(); i++) + { + if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) + { + setValue(i, (TQ_UINT32)0); + } else { + setValue(i, (TQ_UINT32) attr.toUInt()); + } + } + break; + case EXIF_TYPE_RATIONAL: + for(uint i = 0; i < components(); i++) + { + KisExifRational r; + if( (attr = elmt.attribute(TQString("numerator%1").arg(i) ) ).isNull() ) + { + r.numerator = (TQ_UINT32)0; + } else { + r.numerator = (TQ_UINT32) attr.toUInt(); + } + if( (attr = elmt.attribute(TQString("denominator%1").arg(i) ) ).isNull() ) + { + r.denominator = (TQ_UINT32)0; + } else { + r.denominator = (TQ_UINT32) attr.toUInt(); + } + setValue(i, r); + } + break; + case EXIF_TYPE_SBYTE: + for(uint i = 0; i < components(); i++) + { + if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) + { + setValue(i, (TQ_INT8)0); + } else { + setValue(i, (TQ_INT8) attr.toInt()); + } + } + break; + case EXIF_TYPE_UNDEFINED: + { + TQString instr = elmt.attribute("value"); + TQByteArray out; + TQByteArray in = instr.utf8(); + KCodecs::base64Decode( in, out); + out.resize(out.size() - 2 ); + setAsUndefined((uchar*)out.data(), out.size() ); + } + break; + case EXIF_TYPE_SSHORT: + for(uint i = 0; i < components(); i++) + { + if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) + { + setValue(i, (TQ_INT16)0); + } else { + setValue(i, (TQ_INT16) attr.toInt()); + } + } + break; + case EXIF_TYPE_SLONG: + for(uint i = 0; i < components(); i++) + { + if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) + { + setValue(i, (TQ_INT32)0); + } else { + setValue(i, (TQ_INT32) attr.toInt()); + } + } + break; + case EXIF_TYPE_SRATIONAL: + for(uint i = 0; i < components(); i++) + { + KisExifSRational r; + if( (attr = elmt.attribute(TQString("numerator%1").arg(i) ) ).isNull() ) + { + r.numerator = (TQ_INT32)0; + } else { + r.numerator = (TQ_INT32) attr.toInt(); + } + if( (attr = elmt.attribute(TQString("denominator%1").arg(i) ) ).isNull() ) + { + r.denominator = (TQ_UINT32)0; + } else { + r.denominator = (TQ_UINT32) attr.toInt(); + } + setValue(i, r); + } + break; + case EXIF_TYPE_FLOAT: + for(uint i = 0; i < components(); i++) + { + if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) + { + setValue(i, (float)0); + } else { + setValue(i, (float) attr.toFloat()); + } + } + break; + case EXIF_TYPE_DOUBLE: + for(uint i = 0; i < components(); i++) + { + if( (attr = elmt.attribute(TQString("value%1").arg(i) ) ).isNull() ) + { + setValue(i, (double)0); + } else { + setValue(i, (double) attr.toDouble()); + } + } + break; + case EXIF_TYPE_UNKNOW: + break; + + } + return true; +} + +TQDomElement ExifValue::save(TQDomDocument& doc) +{ + TQDomElement elmt = doc.createElement("ExifValue"); + elmt.setAttribute("ifd", ifd()); + elmt.setAttribute("components", components() ); + elmt.setAttribute("type", type() ); + switch(type()) + { + case EXIF_TYPE_BYTE: + for(uint i = 0; i < components(); i++) + elmt.setAttribute(TQString("value%1").arg(i), asByte( i ) ); + break; + case EXIF_TYPE_ASCII: + elmt.setAttribute("value", asAscii() ); + break; + case EXIF_TYPE_SHORT: + for(uint i = 0; i < components(); i++) + elmt.setAttribute(TQString("value%1").arg(i), asShort( i ) ); + break; + case EXIF_TYPE_LONG: + for(uint i = 0; i < components(); i++) + elmt.setAttribute(TQString("value%1").arg(i), asLong( i ) ); + break; + case EXIF_TYPE_RATIONAL: + for(uint i = 0; i < components(); i++) + { + KisExifRational r = asRational(i); + elmt.setAttribute(TQString("numerator%1").arg(i), r.numerator ); + elmt.setAttribute(TQString("denominator%1").arg(i), r.denominator ); + } + break; + case EXIF_TYPE_SBYTE: + for(uint i = 0; i < components(); i++) + elmt.setAttribute(TQString("value%1").arg(i), asSByte( i ) ); + break; + case EXIF_TYPE_UNDEFINED: + { + UByteArray value = asUndefined(); + TQByteArray data; + data.setRawData((char*)value.data(), value.size()); + TQByteArray encodedData; + KCodecs::base64Encode( data, encodedData ); + data.resetRawData( (char*)value.data(), value.size()); + elmt.setAttribute("value", TQString(encodedData)); + } + break; + case EXIF_TYPE_SSHORT: + for(uint i = 0; i < components(); i++) + elmt.setAttribute(TQString("value%1").arg(i), asSShort( i ) ); + break; + case EXIF_TYPE_SLONG: + for(uint i = 0; i < components(); i++) + elmt.setAttribute(TQString("value%1").arg(i), asSLong( i ) ); + break; + case EXIF_TYPE_SRATIONAL: + for(uint i = 0; i < components(); i++) + { + KisExifSRational r = asSRational(i); + elmt.setAttribute(TQString("numerator%1").arg(i), r.numerator ); + elmt.setAttribute(TQString("denominator%1").arg(i), r.denominator ); + } + break; + case EXIF_TYPE_FLOAT: + for(uint i = 0; i < components(); i++) + elmt.setAttribute(TQString("value%1").arg(i), asFloat( i ) ); + break; + case EXIF_TYPE_DOUBLE: + for(uint i = 0; i < components(); i++) + elmt.setAttribute(TQString("value%1").arg(i), asDouble( i ) ); + break; + case EXIF_TYPE_UNKNOW: + break; + } + return elmt; +} + + +void ExifValue::setValue(const unsigned char *data, unsigned int size, ExifValue::ByteOrder order) +{ + switch(type()) + { + case EXIF_TYPE_BYTE: + if( size == components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + n.m_byte = data[i]; + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_ASCII: + setAsAscii((char*) data); + break; + case EXIF_TYPE_SHORT: + if( size == 2*components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + get16Bit( data + 2 * i, order, &n.m_short); + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_LONG: + if( size == 4*components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + get32Bit( data + 4 * i, order, &n.m_long); + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_RATIONAL: + if( size == 8*components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + get32Bit( data + 8 * i, order, &n.m_rational.numerator); + get32Bit( data + 8 * i + 4, order, &n.m_rational.denominator); + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_SBYTE: + if( size == components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + n.m_sbyte = ((TQ_INT8*)data)[i]; + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_UNDEFINED: + setAsUndefined(data, size); + break; + case EXIF_TYPE_SSHORT: + if( size == 2*components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + get16Bit( data + 2 * i, order, (TQ_UINT16*)&n.m_sshort); + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_SLONG: + if( size == 4*components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + get32Bit( data + 4 * i, order, (TQ_UINT32*)&n.m_slong); + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_SRATIONAL: + if( size == 8*components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + get32Bit( data + 8 * i, order, (TQ_UINT32*)&n.m_srational.numerator); + get32Bit( data + 8 * i + 4, order, (TQ_UINT32*)&n.m_srational.denominator); + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_FLOAT: + if( size == 4*components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + get32Bit( data + 4 * i, order, (TQ_UINT32*)&n.m_float); + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_DOUBLE: + if( size == 8*components() ) + { + ExifNumber n; + for(uint i = 0; i < components(); i++) + { + get64Bit( data + 8 * i, order, (TQ_UINT64*)&n.m_double); + setAsExifNumber( i, n); + } + } + break; + case EXIF_TYPE_UNKNOW: + break; + } +} + +void ExifValue::convertToData(unsigned char ** data, unsigned int* size, ExifValue::ByteOrder order) +{ + switch(type()) + { + case EXIF_TYPE_BYTE: + *size = components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + (*data)[i] = asExifNumber(i).m_byte; + } + return; + case EXIF_TYPE_ASCII: + { + TQString str = asAscii(); + *size = str.length(); + *data = new uchar[ *size ]; + uchar* ptr = *data; + memcpy(ptr, str.ascii(), (*size)*sizeof(uchar)); + } + return; + break; + case EXIF_TYPE_SHORT: + { + *size = 2*components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + TQ_UINT16 aenms = asExifNumber(i).m_short; + set16Bit( (*data) + 2 * i, order, &aenms); + } + return; + } + case EXIF_TYPE_LONG: + { + *size = 4*components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + TQ_UINT32 aenml = asExifNumber(i).m_long; + set32Bit( (*data) + 4 * i, order, &aenml); + } + return; + } + case EXIF_TYPE_RATIONAL: + *size = 8*components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + ExifNumber n = asExifNumber(i); + set32Bit( (*data) + 8 * i, order, &n.m_rational.numerator); + set32Bit( (*data) + 8 * i + 4, order, &n.m_rational.denominator); + } + return; + case EXIF_TYPE_SBYTE: + *size = components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + *(((TQ_INT8*)*data) + i) = asExifNumber(i).m_sbyte; + } + return; + case EXIF_TYPE_UNDEFINED: + { + UByteArray array = asUndefined(); + *size = array.size(); + *data = new uchar[*size]; + memcpy( *data, array.data(), (*size)*sizeof(unsigned char)); + } + return; + case EXIF_TYPE_SSHORT: + *size = 2*components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + TQ_INT16 aenmss = asExifNumber(i).m_sshort; + set16Bit( (*data) + 2 * i, order, (TQ_UINT16*)&aenmss); + } + return; + case EXIF_TYPE_SLONG: + *size = 4*components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + TQ_INT32 aenmsl = asExifNumber(i).m_slong; + set32Bit( (*data) + 4 * i, order, (TQ_UINT32*)&aenmsl); + } + return; + case EXIF_TYPE_SRATIONAL: + *size = 8*components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + ExifNumber n = asExifNumber(i); + TQ_INT32 aenmsr_numerator = asExifNumber(i).m_srational.numerator; + TQ_INT32 aenmsr_denominator = asExifNumber(i).m_srational.denominator; + set32Bit( (*data) + 4 * i, order, (TQ_UINT32*)&aenmsr_numerator); + set32Bit( (*data) + 4 * i + 4, order, (TQ_UINT32*)&aenmsr_denominator); + } + return; + case EXIF_TYPE_FLOAT: + *size = 4*components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + float aenmf = asExifNumber(i).m_float; + set32Bit( (*data) + 4 * i, order, (TQ_UINT32*)&aenmf); + } + return; + case EXIF_TYPE_DOUBLE: + *size = 8*components(); + *data = new uchar[*size]; + for(uint i = 0; i < components(); i++) + { + double aenmd = asExifNumber(i).m_double; + set64Bit( (*data) + 4 * i, order, (TQ_UINT64*)&aenmd); + } + return; + case EXIF_TYPE_UNKNOW: + break; + } +} + +TQString ExifValue::toString() +{ + switch(type()) + { + case EXIF_TYPE_ASCII: + return asAscii(); + case EXIF_TYPE_UNDEFINED: + { + TQString undefined = "undefined"; + UByteArray array = asUndefined(); + for(uint i = 0; i < components(); i++) + { + undefined += "\\" + TQString().setNum( array[i] ); + } + return undefined; + } + default: + { + TQString str = ""; + for(uint i = 0; i < components(); i++) + { + str += toString(i); + } + return str; + } + } +} + +TQString ExifValue::toString(uint i) +{ + switch(type()) + { + case EXIF_TYPE_BYTE: + return TQString("%1 ").arg( asExifNumber( i ).m_byte ); + case EXIF_TYPE_SHORT: + return TQString("%1 ").arg( asExifNumber( i ).m_short ); + case EXIF_TYPE_LONG: + return TQString("%1 ").arg( asExifNumber( i ).m_long ); + case EXIF_TYPE_RATIONAL: + return TQString("%1 / %2 ").arg( asExifNumber( i ).m_rational.numerator ).arg( asExifNumber( i ).m_rational.denominator ); + case EXIF_TYPE_SBYTE: { + /* workaround to compiler bug on Raspbian Wheezy */ + TQ_INT8 o_sbyte = asExifNumber( i ).m_sbyte; + return TQString("%1 ").arg( o_sbyte ); + } + case EXIF_TYPE_SSHORT: + return TQString("%1 ").arg( asExifNumber( i ).m_sshort ); + case EXIF_TYPE_SLONG: + return TQString("%1 ").arg( asExifNumber( i ).m_slong ); + case EXIF_TYPE_SRATIONAL: + return TQString("%1 / %2 ").arg( asExifNumber( i ).m_srational.numerator ).arg( asExifNumber( i ).m_srational.denominator ); + case EXIF_TYPE_FLOAT: + return TQString("%1 ").arg( asExifNumber( i ).m_float ); + case EXIF_TYPE_DOUBLE: + return TQString("%1 ").arg( asExifNumber( i ).m_double ); + default: + return "unknow "; + } +} + diff --git a/chalk/core/kis_fill_painter.cc b/chalk/core/kis_fill_painter.cc deleted file mode 100644 index 6b88759a..00000000 --- a/chalk/core/kis_fill_painter.cc +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (c) 2004 Adrian Page - * Copyright (c) 2004 Bart Coppens - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include - -#include "tqbrush.h" -#include "tqfontinfo.h" -#include "tqfontmetrics.h" -#include "tqpen.h" -#include "tqregion.h" -#include "tqwmatrix.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "kis_brush.h" -#include "kis_debug_areas.h" -#include "kis_image.h" -#include "kis_layer.h" -#include "kis_paint_device.h" -#include "kis_painter.h" -#include "kis_pattern.h" -#include "kis_rect.h" -#include "kis_colorspace.h" -#include "kis_transaction.h" -#include "kis_types.h" -#include "kis_vec.h" -#include "kis_selection.h" -#include "kis_fill_painter.h" -#include "kis_iterators_pixel.h" -#include "kis_iterator.h" -#include "kis_color.h" -#include "kis_selection.h" - -namespace { -} - -KisFillPainter::KisFillPainter() - : super() -{ - m_width = m_height = -1; - m_sampleMerged = false; - m_careForSelection = false; - m_fuzzy = false; -} - -KisFillPainter::KisFillPainter(KisPaintDeviceSP device) : super(device) -{ - m_width = m_height = -1; - m_sampleMerged = false; - m_careForSelection = false; - m_fuzzy = false; -} - -// 'regular' filling -// XXX: This also needs renaming, since filling ought to keep the opacity and the composite op in mind, -// this is more eraseToColor. -void KisFillPainter::fillRect(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, const KisColor& kc, TQ_UINT8 opacity) -{ - if (w > 0 && h > 0) { - // Make sure we're in the right colorspace - - KisColor kc2(kc); // get rid of const - kc2.convertTo(m_device->colorSpace()); - TQ_UINT8 * data = kc2.data(); - m_device->colorSpace()->setAlpha(data, opacity, 1); - - m_device->fill(x1, y1, w, h, data); - - addDirtyRect(TQRect(x1, y1, w, h)); - } -} - -void KisFillPainter::fillRect(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, KisPattern * pattern) { - if (!pattern) return; - if (!pattern->valid()) return; - if (!m_device) return; - - - KisPaintDeviceSP patternLayer = pattern->image(m_device->colorSpace()); - - int sx, sy, sw, sh; - - int y = y1; - - if (y >= 0) { - sy = y % pattern->height(); - } else { - sy = pattern->height() - (((-y - 1) % pattern->height()) + 1); - } - - while (y < y1 + h) { - sh = TQMIN((y1 + h) - y, pattern->height() - sy); - - int x = x1; - - if (x >= 0) { - sx = x % pattern->width(); - } else { - sx = pattern->width() - (((-x - 1) % pattern->width()) + 1); - } - - while (x < x1 + w) { - sw = TQMIN((x1 + w) - x, pattern->width() - sx); - - bitBlt(x, y, m_compositeOp, patternLayer.data(), m_opacity, sx, sy, sw, sh); - x += sw; sx = 0; - } - - y+=sh; sy = 0; - } - - addDirtyRect(TQRect(x1, y1, w, h)); -} - -// flood filling - -void KisFillPainter::fillColor(int startX, int startY) { - genericFillStart(startX, startY); - - // Now create a layer and fill it - KisPaintDeviceSP filled = new KisPaintDevice(m_device->colorSpace(), "filled"); - TQ_CHECK_PTR(filled); - KisFillPainter painter(filled.data()); - painter.fillRect(0, 0, m_width, m_height, m_paintColor); - painter.end(); - - genericFillEnd(filled); -} - -void KisFillPainter::fillPattern(int startX, int startY) { - genericFillStart(startX, startY); - - // Now create a layer and fill it - KisPaintDeviceSP filled = new KisPaintDevice(m_device->colorSpace(), "filled"); - TQ_CHECK_PTR(filled); - KisFillPainter painter(filled.data()); - painter.fillRect(0, 0, m_width, m_height, m_pattern); - painter.end(); - - genericFillEnd(filled); -} - -void KisFillPainter::genericFillStart(int startX, int startY) { - m_cancelRequested = false; - - if (m_width < 0 || m_height < 0) { - if (m_device->image()) { - m_width = m_device->image()->width(); - m_height = m_device->image()->height(); - } else { - m_width = m_height = 500; - } - } - - m_size = m_width * m_height; - - // Create a selection from the surrounding area - m_selection = createFloodSelection(startX, startY); -} - -void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled) { - if (m_cancelRequested) { - m_width = m_height = -1; - return; - } - - TQRect rc = m_selection->selectedRect(); - - bltSelection(rc.x(), rc.y(), m_compositeOp, filled, m_selection, m_opacity, - rc.x(), rc.y(), rc.width(), rc.height()); - - emit notifyProgressDone(); - - m_width = m_height = -1; -} - -struct FillSegment { - FillSegment(int x, int y/*, FillSegment* parent*/) : x(x), y(y)/*, parent(parent)*/ {} - int x; - int y; -// FillSegment* parent; -}; - -typedef enum { None = 0, Added = 1, Checked = 2 } Status; - -KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY) { - if (m_width < 0 || m_height < 0) { - if (m_device->hasSelection() && m_careForSelection) { - - TQRect rc = m_device->selection()->selectedRect(); - m_width = rc.width() - (startX - rc.x()); - m_height = rc.height() - (startY - rc.y()); - - } else if (m_device->image()) { - - m_width = m_device->image()->width(); - m_height = m_device->image()->height(); - - } else { - m_width = m_height = 500; - } - } - - // Don't try to fill if we start outside the borders, just return an empty 'fill' - if (startX < 0 || startY < 0 || startX >= m_width || startY >= m_height) - return new KisSelection(m_device); - - KisPaintDeviceSP sourceDevice = 0; - - // sample merged? - if (m_sampleMerged) { - if (!m_device->image()) { - return new KisSelection(m_device); - } - sourceDevice = m_device->image()->mergedImage(); - } else { - sourceDevice = m_device; - } - - m_size = m_width * m_height; - - KisSelectionSP selection = new KisSelection(m_device); - KisColorSpace * colorSpace = selection->colorSpace(); - KisColorSpace * devColorSpace = sourceDevice->colorSpace(); - - TQ_UINT8* source = new TQ_UINT8[sourceDevice->pixelSize()]; - KisHLineIteratorPixel pixelIt = sourceDevice->createHLineIterator(startX, startY, startX+1, false); - - memcpy(source, pixelIt.rawData(), sourceDevice->pixelSize()); - - std::stack stack; - - stack.push(new FillSegment(startX, startY/*, 0*/)); - - Status* map = new Status[m_size]; - - memset(map, None, m_size * sizeof(Status)); - - int progressPercent = 0; int pixelsDone = 0; int currentPercent = 0; - emit notifyProgressStage(i18n("Making fill outline..."), 0); - - bool hasSelection = m_careForSelection && sourceDevice->hasSelection(); - KisSelectionSP srcSel = 0; - if (hasSelection) - srcSel = sourceDevice->selection(); - - while(!stack.empty()) { - FillSegment* segment = stack.top(); - stack.pop(); - if (map[m_width * segment->y + segment->x] == Checked) { - delete segment; - continue; - } - map[m_width * segment->y + segment->x] = Checked; - - int x = segment->x; - int y = segment->y; - - /* We need an iterator that is valid in the range (0,y) - (width,y). Therefore, - it is needed to start the iterator at the first position, and then skip to (x,y). */ - pixelIt = sourceDevice->createHLineIterator(0, y, m_width, false); - pixelIt += x; - TQ_UINT8 diff = devColorSpace->difference(source, pixelIt.rawData()); - - if (diff >= m_threshold - || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) { - delete segment; - continue; - } - - // Here as well: start the iterator at (0,y) - KisHLineIteratorPixel selIt = selection->createHLineIterator(0, y, m_width, true); - selIt += x; - if (m_fuzzy) - colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData()); - else - colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData()); - - if (y > 0 && (map[m_width * (y - 1) + x] == None)) { - map[m_width * (y - 1) + x] = Added; - stack.push(new FillSegment(x, y-1)); - } - if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) { - map[m_width * (y + 1) + x] = Added; - stack.push(new FillSegment(x, y+1)); - } - - ++pixelsDone; - - bool stop = false; - - --pixelIt; - --selIt; - --x; - - // go to the left - while(!stop && x >= 0 && (map[m_width * y + x] != Checked) ) { // FIXME optimizeable? - map[m_width * y + x] = Checked; - diff = devColorSpace->difference(source, pixelIt.rawData()); - if (diff >= m_threshold - || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) { - stop = true; - continue; - } - - if (m_fuzzy) - colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData()); - else - colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData()); - - if (y > 0 && (map[m_width * (y - 1) + x] == None)) { - map[m_width * (y - 1) + x] = Added; - stack.push(new FillSegment(x, y-1)); - } - if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) { - map[m_width * (y + 1) + x] = Added; - stack.push(new FillSegment(x, y+1)); - } - ++pixelsDone; - --pixelIt; - --selIt; - --x; - } - - x = segment->x + 1; - delete segment; - - if (map[m_width * y + x] == Checked) - continue; - - // and go to the right - pixelIt = sourceDevice->createHLineIterator(x, y, m_width, false); - selIt = selection->createHLineIterator(x, y, m_width, true); - - stop = false; - while(!stop && x < m_width && (map[m_width * y + x] != Checked) ) { - diff = devColorSpace->difference(source, pixelIt.rawData()); - map[m_width * y + x] = Checked; - - if (diff >= m_threshold - || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED) ) { - stop = true; - continue; - } - - if (m_fuzzy) - colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData()); - else - colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData()); - - if (y > 0 && (map[m_width * (y - 1) + x] == None)) { - map[m_width * (y - 1) + x] = Added; - stack.push(new FillSegment(x, y-1)); - } - if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) { - map[m_width * (y + 1) + x] = Added; - stack.push(new FillSegment(x, y+1)); - } - ++pixelsDone; - ++pixelIt; - ++selIt; - ++x; - } - - if (m_size > 0) { - progressPercent = (pixelsDone * 100) / m_size; - if (progressPercent > currentPercent) { - emit notifyProgress(progressPercent); - currentPercent = progressPercent; - } - } - } - - - delete[] map; - delete[] source; - - return selection; -} diff --git a/chalk/core/kis_fill_painter.cpp b/chalk/core/kis_fill_painter.cpp new file mode 100644 index 00000000..6b88759a --- /dev/null +++ b/chalk/core/kis_fill_painter.cpp @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2004 Adrian Page + * Copyright (c) 2004 Bart Coppens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include "tqbrush.h" +#include "tqfontinfo.h" +#include "tqfontmetrics.h" +#include "tqpen.h" +#include "tqregion.h" +#include "tqwmatrix.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kis_brush.h" +#include "kis_debug_areas.h" +#include "kis_image.h" +#include "kis_layer.h" +#include "kis_paint_device.h" +#include "kis_painter.h" +#include "kis_pattern.h" +#include "kis_rect.h" +#include "kis_colorspace.h" +#include "kis_transaction.h" +#include "kis_types.h" +#include "kis_vec.h" +#include "kis_selection.h" +#include "kis_fill_painter.h" +#include "kis_iterators_pixel.h" +#include "kis_iterator.h" +#include "kis_color.h" +#include "kis_selection.h" + +namespace { +} + +KisFillPainter::KisFillPainter() + : super() +{ + m_width = m_height = -1; + m_sampleMerged = false; + m_careForSelection = false; + m_fuzzy = false; +} + +KisFillPainter::KisFillPainter(KisPaintDeviceSP device) : super(device) +{ + m_width = m_height = -1; + m_sampleMerged = false; + m_careForSelection = false; + m_fuzzy = false; +} + +// 'regular' filling +// XXX: This also needs renaming, since filling ought to keep the opacity and the composite op in mind, +// this is more eraseToColor. +void KisFillPainter::fillRect(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, const KisColor& kc, TQ_UINT8 opacity) +{ + if (w > 0 && h > 0) { + // Make sure we're in the right colorspace + + KisColor kc2(kc); // get rid of const + kc2.convertTo(m_device->colorSpace()); + TQ_UINT8 * data = kc2.data(); + m_device->colorSpace()->setAlpha(data, opacity, 1); + + m_device->fill(x1, y1, w, h, data); + + addDirtyRect(TQRect(x1, y1, w, h)); + } +} + +void KisFillPainter::fillRect(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, KisPattern * pattern) { + if (!pattern) return; + if (!pattern->valid()) return; + if (!m_device) return; + + + KisPaintDeviceSP patternLayer = pattern->image(m_device->colorSpace()); + + int sx, sy, sw, sh; + + int y = y1; + + if (y >= 0) { + sy = y % pattern->height(); + } else { + sy = pattern->height() - (((-y - 1) % pattern->height()) + 1); + } + + while (y < y1 + h) { + sh = TQMIN((y1 + h) - y, pattern->height() - sy); + + int x = x1; + + if (x >= 0) { + sx = x % pattern->width(); + } else { + sx = pattern->width() - (((-x - 1) % pattern->width()) + 1); + } + + while (x < x1 + w) { + sw = TQMIN((x1 + w) - x, pattern->width() - sx); + + bitBlt(x, y, m_compositeOp, patternLayer.data(), m_opacity, sx, sy, sw, sh); + x += sw; sx = 0; + } + + y+=sh; sy = 0; + } + + addDirtyRect(TQRect(x1, y1, w, h)); +} + +// flood filling + +void KisFillPainter::fillColor(int startX, int startY) { + genericFillStart(startX, startY); + + // Now create a layer and fill it + KisPaintDeviceSP filled = new KisPaintDevice(m_device->colorSpace(), "filled"); + TQ_CHECK_PTR(filled); + KisFillPainter painter(filled.data()); + painter.fillRect(0, 0, m_width, m_height, m_paintColor); + painter.end(); + + genericFillEnd(filled); +} + +void KisFillPainter::fillPattern(int startX, int startY) { + genericFillStart(startX, startY); + + // Now create a layer and fill it + KisPaintDeviceSP filled = new KisPaintDevice(m_device->colorSpace(), "filled"); + TQ_CHECK_PTR(filled); + KisFillPainter painter(filled.data()); + painter.fillRect(0, 0, m_width, m_height, m_pattern); + painter.end(); + + genericFillEnd(filled); +} + +void KisFillPainter::genericFillStart(int startX, int startY) { + m_cancelRequested = false; + + if (m_width < 0 || m_height < 0) { + if (m_device->image()) { + m_width = m_device->image()->width(); + m_height = m_device->image()->height(); + } else { + m_width = m_height = 500; + } + } + + m_size = m_width * m_height; + + // Create a selection from the surrounding area + m_selection = createFloodSelection(startX, startY); +} + +void KisFillPainter::genericFillEnd(KisPaintDeviceSP filled) { + if (m_cancelRequested) { + m_width = m_height = -1; + return; + } + + TQRect rc = m_selection->selectedRect(); + + bltSelection(rc.x(), rc.y(), m_compositeOp, filled, m_selection, m_opacity, + rc.x(), rc.y(), rc.width(), rc.height()); + + emit notifyProgressDone(); + + m_width = m_height = -1; +} + +struct FillSegment { + FillSegment(int x, int y/*, FillSegment* parent*/) : x(x), y(y)/*, parent(parent)*/ {} + int x; + int y; +// FillSegment* parent; +}; + +typedef enum { None = 0, Added = 1, Checked = 2 } Status; + +KisSelectionSP KisFillPainter::createFloodSelection(int startX, int startY) { + if (m_width < 0 || m_height < 0) { + if (m_device->hasSelection() && m_careForSelection) { + + TQRect rc = m_device->selection()->selectedRect(); + m_width = rc.width() - (startX - rc.x()); + m_height = rc.height() - (startY - rc.y()); + + } else if (m_device->image()) { + + m_width = m_device->image()->width(); + m_height = m_device->image()->height(); + + } else { + m_width = m_height = 500; + } + } + + // Don't try to fill if we start outside the borders, just return an empty 'fill' + if (startX < 0 || startY < 0 || startX >= m_width || startY >= m_height) + return new KisSelection(m_device); + + KisPaintDeviceSP sourceDevice = 0; + + // sample merged? + if (m_sampleMerged) { + if (!m_device->image()) { + return new KisSelection(m_device); + } + sourceDevice = m_device->image()->mergedImage(); + } else { + sourceDevice = m_device; + } + + m_size = m_width * m_height; + + KisSelectionSP selection = new KisSelection(m_device); + KisColorSpace * colorSpace = selection->colorSpace(); + KisColorSpace * devColorSpace = sourceDevice->colorSpace(); + + TQ_UINT8* source = new TQ_UINT8[sourceDevice->pixelSize()]; + KisHLineIteratorPixel pixelIt = sourceDevice->createHLineIterator(startX, startY, startX+1, false); + + memcpy(source, pixelIt.rawData(), sourceDevice->pixelSize()); + + std::stack stack; + + stack.push(new FillSegment(startX, startY/*, 0*/)); + + Status* map = new Status[m_size]; + + memset(map, None, m_size * sizeof(Status)); + + int progressPercent = 0; int pixelsDone = 0; int currentPercent = 0; + emit notifyProgressStage(i18n("Making fill outline..."), 0); + + bool hasSelection = m_careForSelection && sourceDevice->hasSelection(); + KisSelectionSP srcSel = 0; + if (hasSelection) + srcSel = sourceDevice->selection(); + + while(!stack.empty()) { + FillSegment* segment = stack.top(); + stack.pop(); + if (map[m_width * segment->y + segment->x] == Checked) { + delete segment; + continue; + } + map[m_width * segment->y + segment->x] = Checked; + + int x = segment->x; + int y = segment->y; + + /* We need an iterator that is valid in the range (0,y) - (width,y). Therefore, + it is needed to start the iterator at the first position, and then skip to (x,y). */ + pixelIt = sourceDevice->createHLineIterator(0, y, m_width, false); + pixelIt += x; + TQ_UINT8 diff = devColorSpace->difference(source, pixelIt.rawData()); + + if (diff >= m_threshold + || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) { + delete segment; + continue; + } + + // Here as well: start the iterator at (0,y) + KisHLineIteratorPixel selIt = selection->createHLineIterator(0, y, m_width, true); + selIt += x; + if (m_fuzzy) + colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData()); + else + colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData()); + + if (y > 0 && (map[m_width * (y - 1) + x] == None)) { + map[m_width * (y - 1) + x] = Added; + stack.push(new FillSegment(x, y-1)); + } + if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) { + map[m_width * (y + 1) + x] = Added; + stack.push(new FillSegment(x, y+1)); + } + + ++pixelsDone; + + bool stop = false; + + --pixelIt; + --selIt; + --x; + + // go to the left + while(!stop && x >= 0 && (map[m_width * y + x] != Checked) ) { // FIXME optimizeable? + map[m_width * y + x] = Checked; + diff = devColorSpace->difference(source, pixelIt.rawData()); + if (diff >= m_threshold + || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED)) { + stop = true; + continue; + } + + if (m_fuzzy) + colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData()); + else + colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData()); + + if (y > 0 && (map[m_width * (y - 1) + x] == None)) { + map[m_width * (y - 1) + x] = Added; + stack.push(new FillSegment(x, y-1)); + } + if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) { + map[m_width * (y + 1) + x] = Added; + stack.push(new FillSegment(x, y+1)); + } + ++pixelsDone; + --pixelIt; + --selIt; + --x; + } + + x = segment->x + 1; + delete segment; + + if (map[m_width * y + x] == Checked) + continue; + + // and go to the right + pixelIt = sourceDevice->createHLineIterator(x, y, m_width, false); + selIt = selection->createHLineIterator(x, y, m_width, true); + + stop = false; + while(!stop && x < m_width && (map[m_width * y + x] != Checked) ) { + diff = devColorSpace->difference(source, pixelIt.rawData()); + map[m_width * y + x] = Checked; + + if (diff >= m_threshold + || (hasSelection && srcSel->selected(pixelIt.x(), pixelIt.y()) == MIN_SELECTED) ) { + stop = true; + continue; + } + + if (m_fuzzy) + colorSpace->fromTQColor(TQt::white, MAX_SELECTED - diff, selIt.rawData()); + else + colorSpace->fromTQColor(TQt::white, MAX_SELECTED, selIt.rawData()); + + if (y > 0 && (map[m_width * (y - 1) + x] == None)) { + map[m_width * (y - 1) + x] = Added; + stack.push(new FillSegment(x, y-1)); + } + if (y < (m_height - 1) && (map[m_width * (y + 1) + x] == None)) { + map[m_width * (y + 1) + x] = Added; + stack.push(new FillSegment(x, y+1)); + } + ++pixelsDone; + ++pixelIt; + ++selIt; + ++x; + } + + if (m_size > 0) { + progressPercent = (pixelsDone * 100) / m_size; + if (progressPercent > currentPercent) { + emit notifyProgress(progressPercent); + currentPercent = progressPercent; + } + } + } + + + delete[] map; + delete[] source; + + return selection; +} diff --git a/chalk/core/kis_filter.cc b/chalk/core/kis_filter.cc deleted file mode 100644 index ee396596..00000000 --- a/chalk/core/kis_filter.cc +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2004 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_filter.h" - -#include - -#include "kis_types.h" -#include "kis_filter_configuration.h" - -KisFilter::KisFilter(const KisID& id, const TQString & category, const TQString & entry) - : KisProgressSubject(0, id.id().latin1()) - , m_id(id) - , m_progressDisplay(0) - , m_category(category) - , m_entry(entry) -{ -} - -KisFilterConfiguration * KisFilter::configuration(TQWidget*) -{ - return new KisFilterConfiguration(m_id.id(), 0); -} - -KisFilterConfiguration * KisFilter::configuration() -{ - return new KisFilterConfiguration(m_id.id(), 0); -} - -KisFilterConfigWidget * KisFilter::createConfigurationWidget(TQWidget *, KisPaintDeviceSP) -{ - return 0; -} - -void KisFilter::setProgressDisplay(KisProgressDisplayInterface * progressDisplay) -{ - m_progressDisplay = progressDisplay; -} - - -void KisFilter::enableProgress() { - m_progressEnabled = true; - m_cancelRequested = false; -} - -void KisFilter::disableProgress() { - m_progressEnabled = false; - m_cancelRequested = false; - m_progressDisplay = 0; -} - -void KisFilter::setProgressTotalSteps(TQ_INT32 totalSteps) -{ - if (m_progressEnabled) { - - m_progressTotalSteps = totalSteps; - m_lastProgressPerCent = 0; - m_progressSteps = 0; - emit notifyProgress(0); - } -} - -void KisFilter::setProgress(TQ_INT32 progress) -{ - if (m_progressEnabled) { - TQ_INT32 progressPerCent = (progress * 100) / m_progressTotalSteps; - m_progressSteps = progress; - - if (progressPerCent != m_lastProgressPerCent) { - - m_lastProgressPerCent = progressPerCent; - emit notifyProgress(progressPerCent); - } - } -} - -void KisFilter::incProgress() -{ - setProgress(++m_progressSteps); - -} - -void KisFilter::setProgressStage(const TQString& stage, TQ_INT32 progress) -{ - if (m_progressEnabled) { - - TQ_INT32 progressPerCent = (progress * 100) / m_progressTotalSteps; - - m_lastProgressPerCent = progressPerCent; - emit notifyProgressStage(stage, progressPerCent); - } -} - -void KisFilter::setProgressDone() -{ - if (m_progressEnabled) { - emit notifyProgressDone(); - } -} - - -bool KisFilter::autoUpdate() { - return m_autoUpdate; -} - -void KisFilter::setAutoUpdate(bool set) { - m_autoUpdate = set; -} - -TQRect KisFilter::enlargeRect(TQRect rect, KisFilterConfiguration* c) const { - int margin = overlapMarginNeeded(c); - rect.rLeft() -= margin; - rect.rTop() -= margin; - rect.rRight() += margin; - rect.rBottom() += margin; - return rect; -} - -#include "kis_filter.moc" diff --git a/chalk/core/kis_filter.cpp b/chalk/core/kis_filter.cpp new file mode 100644 index 00000000..ee396596 --- /dev/null +++ b/chalk/core/kis_filter.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2004 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_filter.h" + +#include + +#include "kis_types.h" +#include "kis_filter_configuration.h" + +KisFilter::KisFilter(const KisID& id, const TQString & category, const TQString & entry) + : KisProgressSubject(0, id.id().latin1()) + , m_id(id) + , m_progressDisplay(0) + , m_category(category) + , m_entry(entry) +{ +} + +KisFilterConfiguration * KisFilter::configuration(TQWidget*) +{ + return new KisFilterConfiguration(m_id.id(), 0); +} + +KisFilterConfiguration * KisFilter::configuration() +{ + return new KisFilterConfiguration(m_id.id(), 0); +} + +KisFilterConfigWidget * KisFilter::createConfigurationWidget(TQWidget *, KisPaintDeviceSP) +{ + return 0; +} + +void KisFilter::setProgressDisplay(KisProgressDisplayInterface * progressDisplay) +{ + m_progressDisplay = progressDisplay; +} + + +void KisFilter::enableProgress() { + m_progressEnabled = true; + m_cancelRequested = false; +} + +void KisFilter::disableProgress() { + m_progressEnabled = false; + m_cancelRequested = false; + m_progressDisplay = 0; +} + +void KisFilter::setProgressTotalSteps(TQ_INT32 totalSteps) +{ + if (m_progressEnabled) { + + m_progressTotalSteps = totalSteps; + m_lastProgressPerCent = 0; + m_progressSteps = 0; + emit notifyProgress(0); + } +} + +void KisFilter::setProgress(TQ_INT32 progress) +{ + if (m_progressEnabled) { + TQ_INT32 progressPerCent = (progress * 100) / m_progressTotalSteps; + m_progressSteps = progress; + + if (progressPerCent != m_lastProgressPerCent) { + + m_lastProgressPerCent = progressPerCent; + emit notifyProgress(progressPerCent); + } + } +} + +void KisFilter::incProgress() +{ + setProgress(++m_progressSteps); + +} + +void KisFilter::setProgressStage(const TQString& stage, TQ_INT32 progress) +{ + if (m_progressEnabled) { + + TQ_INT32 progressPerCent = (progress * 100) / m_progressTotalSteps; + + m_lastProgressPerCent = progressPerCent; + emit notifyProgressStage(stage, progressPerCent); + } +} + +void KisFilter::setProgressDone() +{ + if (m_progressEnabled) { + emit notifyProgressDone(); + } +} + + +bool KisFilter::autoUpdate() { + return m_autoUpdate; +} + +void KisFilter::setAutoUpdate(bool set) { + m_autoUpdate = set; +} + +TQRect KisFilter::enlargeRect(TQRect rect, KisFilterConfiguration* c) const { + int margin = overlapMarginNeeded(c); + rect.rLeft() -= margin; + rect.rTop() -= margin; + rect.rRight() += margin; + rect.rBottom() += margin; + return rect; +} + +#include "kis_filter.moc" diff --git a/chalk/core/kis_filter_config_widget.cc b/chalk/core/kis_filter_config_widget.cc deleted file mode 100644 index 1df05f64..00000000 --- a/chalk/core/kis_filter_config_widget.cc +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "kis_filter_config_widget.h" - - -KisFilterConfigWidget::KisFilterConfigWidget(TQWidget * parent, const char * name, WFlags f) - : TQWidget(parent, name, f) -{ -} - -KisFilterConfigWidget::~KisFilterConfigWidget() -{ -} - -#include "kis_filter_config_widget.moc" diff --git a/chalk/core/kis_filter_config_widget.cpp b/chalk/core/kis_filter_config_widget.cpp new file mode 100644 index 00000000..1df05f64 --- /dev/null +++ b/chalk/core/kis_filter_config_widget.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt (boud@valdyas.org) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_filter_config_widget.h" + + +KisFilterConfigWidget::KisFilterConfigWidget(TQWidget * parent, const char * name, WFlags f) + : TQWidget(parent, name, f) +{ +} + +KisFilterConfigWidget::~KisFilterConfigWidget() +{ +} + +#include "kis_filter_config_widget.moc" diff --git a/chalk/core/kis_filter_configuration.cc b/chalk/core/kis_filter_configuration.cc deleted file mode 100644 index b29a20b8..00000000 --- a/chalk/core/kis_filter_configuration.cc +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Copyright (c) 2006 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_filter.h" - -#include -#include -#include - -#include "kis_filter_registry.h" -#include "kis_transaction.h" -#include "kis_undo_adapter.h" -#include "kis_painter.h" -#include "kis_selection.h" -#include "kis_id.h" -#include "kis_canvas_subject.h" -#include "kis_progress_display_interface.h" -#include "kis_types.h" -#include "kis_filter_config_widget.h" - - -KisFilterConfiguration::KisFilterConfiguration(const KisFilterConfiguration & rhs) -{ - m_name = rhs.m_name; - m_version = rhs.m_version; - m_properties = rhs.m_properties; -} - -void KisFilterConfiguration::fromXML(const TQString & s ) -{ - m_properties.clear(); - - TQDomDocument doc; - doc.setContent( s ); - TQDomElement e = doc.documentElement(); - TQDomNode n = e.firstChild(); - - m_name = e.attribute("name"); - m_version = e.attribute("version").toInt(); - - while (!n.isNull()) { - // We don't nest elements in filter configuration. For now... - TQDomElement e = n.toElement(); - TQString name; - TQString type; - TQString value; - - if (!e.isNull()) { - if (e.tagName() == "property") { - name = e.attribute("name"); - type = e.attribute("type"); - value = e.text(); - // XXX Convert the variant pro-actively to the right type? - m_properties[name] = TQVariant(value); - } - } - n = n.nextSibling(); - } - //dump(); -} - -TQString KisFilterConfiguration::toString() -{ - TQDomDocument doc = TQDomDocument("filterconfig"); - TQDomElement root = doc.createElement( "filterconfig" ); - root.setAttribute( "name", m_name ); - root.setAttribute( "version", m_version ); - - doc.appendChild( root ); - - TQStringVariantMap::Iterator it; - for ( it = m_properties.begin(); it != m_properties.end(); ++it ) { - TQDomElement e = doc.createElement( "property" ); - e.setAttribute( "name", it.key().latin1() ); - TQVariant v = it.data(); - e.setAttribute( "type", v.typeName() ); - TQString s = v.asString(); - TQDomText text = doc.createCDATASection(v.asString() ); // XXX: Unittest this! - e.appendChild(text); - root.appendChild(e); - } - - return doc.toString(); -} - -const TQString & KisFilterConfiguration::name() const -{ - return m_name; -} - -TQ_INT32 KisFilterConfiguration::version() const -{ - return m_version; -} - -void KisFilterConfiguration::setProperty(const TQString & name, const TQVariant & value) -{ - if ( m_properties.find( name ) == m_properties.end() ) { - m_properties.insert( name, value ); - } - else { - m_properties[name] = value; - } -} - -bool KisFilterConfiguration::getProperty(const TQString & name, TQVariant & value) -{ - if ( m_properties.find( name ) == m_properties.end() ) { - return false; - } - else { - value = m_properties[name]; - return true; - } -} - -TQVariant KisFilterConfiguration::getProperty(const TQString & name) -{ - if ( m_properties.find( name ) == m_properties.end() ) { - return TQVariant(); - } - else { - return m_properties[name]; - } -} - - -int KisFilterConfiguration::getInt(const TQString & name, int def) -{ - TQVariant v = getProperty(name); - if (v.isValid()) - return v.asInt(); - else - return def; - -} - -double KisFilterConfiguration::getDouble(const TQString & name, double def) -{ - TQVariant v = getProperty(name); - if (v.isValid()) - return v.asDouble(); - else - return def; -} - -bool KisFilterConfiguration::getBool(const TQString & name, bool def) -{ - TQVariant v = getProperty(name); - if (v.isValid()) - return v.asBool(); - else - return def; -} - -TQString KisFilterConfiguration::getString(const TQString & name, TQString def) -{ - TQVariant v = getProperty(name); - if (v.isValid()) - return v.asString(); - else - return def; -} - -void KisFilterConfiguration::dump() -{ - TQStringVariantMap::Iterator it; - for ( it = m_properties.begin(); it != m_properties.end(); ++it ) { - } - -} diff --git a/chalk/core/kis_filter_configuration.cpp b/chalk/core/kis_filter_configuration.cpp new file mode 100644 index 00000000..b29a20b8 --- /dev/null +++ b/chalk/core/kis_filter_configuration.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2006 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_filter.h" + +#include +#include +#include + +#include "kis_filter_registry.h" +#include "kis_transaction.h" +#include "kis_undo_adapter.h" +#include "kis_painter.h" +#include "kis_selection.h" +#include "kis_id.h" +#include "kis_canvas_subject.h" +#include "kis_progress_display_interface.h" +#include "kis_types.h" +#include "kis_filter_config_widget.h" + + +KisFilterConfiguration::KisFilterConfiguration(const KisFilterConfiguration & rhs) +{ + m_name = rhs.m_name; + m_version = rhs.m_version; + m_properties = rhs.m_properties; +} + +void KisFilterConfiguration::fromXML(const TQString & s ) +{ + m_properties.clear(); + + TQDomDocument doc; + doc.setContent( s ); + TQDomElement e = doc.documentElement(); + TQDomNode n = e.firstChild(); + + m_name = e.attribute("name"); + m_version = e.attribute("version").toInt(); + + while (!n.isNull()) { + // We don't nest elements in filter configuration. For now... + TQDomElement e = n.toElement(); + TQString name; + TQString type; + TQString value; + + if (!e.isNull()) { + if (e.tagName() == "property") { + name = e.attribute("name"); + type = e.attribute("type"); + value = e.text(); + // XXX Convert the variant pro-actively to the right type? + m_properties[name] = TQVariant(value); + } + } + n = n.nextSibling(); + } + //dump(); +} + +TQString KisFilterConfiguration::toString() +{ + TQDomDocument doc = TQDomDocument("filterconfig"); + TQDomElement root = doc.createElement( "filterconfig" ); + root.setAttribute( "name", m_name ); + root.setAttribute( "version", m_version ); + + doc.appendChild( root ); + + TQStringVariantMap::Iterator it; + for ( it = m_properties.begin(); it != m_properties.end(); ++it ) { + TQDomElement e = doc.createElement( "property" ); + e.setAttribute( "name", it.key().latin1() ); + TQVariant v = it.data(); + e.setAttribute( "type", v.typeName() ); + TQString s = v.asString(); + TQDomText text = doc.createCDATASection(v.asString() ); // XXX: Unittest this! + e.appendChild(text); + root.appendChild(e); + } + + return doc.toString(); +} + +const TQString & KisFilterConfiguration::name() const +{ + return m_name; +} + +TQ_INT32 KisFilterConfiguration::version() const +{ + return m_version; +} + +void KisFilterConfiguration::setProperty(const TQString & name, const TQVariant & value) +{ + if ( m_properties.find( name ) == m_properties.end() ) { + m_properties.insert( name, value ); + } + else { + m_properties[name] = value; + } +} + +bool KisFilterConfiguration::getProperty(const TQString & name, TQVariant & value) +{ + if ( m_properties.find( name ) == m_properties.end() ) { + return false; + } + else { + value = m_properties[name]; + return true; + } +} + +TQVariant KisFilterConfiguration::getProperty(const TQString & name) +{ + if ( m_properties.find( name ) == m_properties.end() ) { + return TQVariant(); + } + else { + return m_properties[name]; + } +} + + +int KisFilterConfiguration::getInt(const TQString & name, int def) +{ + TQVariant v = getProperty(name); + if (v.isValid()) + return v.asInt(); + else + return def; + +} + +double KisFilterConfiguration::getDouble(const TQString & name, double def) +{ + TQVariant v = getProperty(name); + if (v.isValid()) + return v.asDouble(); + else + return def; +} + +bool KisFilterConfiguration::getBool(const TQString & name, bool def) +{ + TQVariant v = getProperty(name); + if (v.isValid()) + return v.asBool(); + else + return def; +} + +TQString KisFilterConfiguration::getString(const TQString & name, TQString def) +{ + TQVariant v = getProperty(name); + if (v.isValid()) + return v.asString(); + else + return def; +} + +void KisFilterConfiguration::dump() +{ + TQStringVariantMap::Iterator it; + for ( it = m_properties.begin(); it != m_properties.end(); ++it ) { + } + +} diff --git a/chalk/core/kis_filter_registry.cc b/chalk/core/kis_filter_registry.cc deleted file mode 100644 index f67e3ecc..00000000 --- a/chalk/core/kis_filter_registry.cc +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2003 Patrick Julien - * Copyright (c) 2004 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include "kis_debug_areas.h" -#include -#include "kis_types.h" -#include "kis_filter_registry.h" -#include "kis_paint_device.h" -#include "kis_filter.h" - -KisFilterRegistry *KisFilterRegistry::m_singleton = 0; - -KisFilterRegistry::KisFilterRegistry() -{ - Q_ASSERT(KisFilterRegistry::m_singleton == 0); - KisFilterRegistry::m_singleton = this; - - TDETrader::OfferList offers = TDETrader::self()->query(TQString::fromLatin1("Chalk/Filter"), - TQString::fromLatin1("(Type == 'Service') and " - "([X-Chalk-Version] == 2)")); - - TDETrader::OfferList::ConstIterator iter; - - for(iter = offers.begin(); iter != offers.end(); ++iter) - { - KService::Ptr service = *iter; - int errCode = 0; - KParts::Plugin* plugin = - KParts::ComponentFactory::createInstanceFromService ( service, this, 0, TQStringList(), &errCode); - if ( plugin ) - kdDebug(DBG_AREA_PLUGINS) << "found plugin " << service->property("Name").toString() << "\n"; - else { - kdDebug(41006) << "found plugin " << service->property("Name").toString() << ", " << errCode << "\n"; - if( errCode == KParts::ComponentFactory::ErrNoLibrary) - { - kdWarning(41006) << " Error loading plugin was : ErrNoLibrary " << KLibLoader::self()->lastErrorMessage() << endl; - } - } - - } - -} - -KisFilterRegistry::~KisFilterRegistry() -{ -} - -KisFilterRegistry* KisFilterRegistry::instance() -{ - if(KisFilterRegistry::m_singleton == 0) - { - KisFilterRegistry::m_singleton = new KisFilterRegistry(); - } - return KisFilterRegistry::m_singleton; -} - -#include "kis_filter_registry.moc" diff --git a/chalk/core/kis_filter_registry.cpp b/chalk/core/kis_filter_registry.cpp new file mode 100644 index 00000000..f67e3ecc --- /dev/null +++ b/chalk/core/kis_filter_registry.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003 Patrick Julien + * Copyright (c) 2004 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "kis_debug_areas.h" +#include +#include "kis_types.h" +#include "kis_filter_registry.h" +#include "kis_paint_device.h" +#include "kis_filter.h" + +KisFilterRegistry *KisFilterRegistry::m_singleton = 0; + +KisFilterRegistry::KisFilterRegistry() +{ + Q_ASSERT(KisFilterRegistry::m_singleton == 0); + KisFilterRegistry::m_singleton = this; + + TDETrader::OfferList offers = TDETrader::self()->query(TQString::fromLatin1("Chalk/Filter"), + TQString::fromLatin1("(Type == 'Service') and " + "([X-Chalk-Version] == 2)")); + + TDETrader::OfferList::ConstIterator iter; + + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + int errCode = 0; + KParts::Plugin* plugin = + KParts::ComponentFactory::createInstanceFromService ( service, this, 0, TQStringList(), &errCode); + if ( plugin ) + kdDebug(DBG_AREA_PLUGINS) << "found plugin " << service->property("Name").toString() << "\n"; + else { + kdDebug(41006) << "found plugin " << service->property("Name").toString() << ", " << errCode << "\n"; + if( errCode == KParts::ComponentFactory::ErrNoLibrary) + { + kdWarning(41006) << " Error loading plugin was : ErrNoLibrary " << KLibLoader::self()->lastErrorMessage() << endl; + } + } + + } + +} + +KisFilterRegistry::~KisFilterRegistry() +{ +} + +KisFilterRegistry* KisFilterRegistry::instance() +{ + if(KisFilterRegistry::m_singleton == 0) + { + KisFilterRegistry::m_singleton = new KisFilterRegistry(); + } + return KisFilterRegistry::m_singleton; +} + +#include "kis_filter_registry.moc" diff --git a/chalk/core/kis_filter_strategy.cc b/chalk/core/kis_filter_strategy.cc deleted file mode 100644 index 650a9681..00000000 --- a/chalk/core/kis_filter_strategy.cc +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2004 Michael Thaler - * Copyright (c) 2005 Casper Boemann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include -#include "kis_debug_areas.h" -#include "kis_filter_strategy.h" -#include - -double KisHermiteFilterStrategy::valueAt(double t) const { - /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ - if(t < 0.0) t = -t; - if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0); - return(0.0); -} - -TQ_UINT32 KisHermiteFilterStrategy::intValueAt(TQ_INT32 t) const { - /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ - if(t < 0) t = -t; - if(t < 256) - { - t =(2 * t - 3*256) * t * t +(256<<16); - - //go from .24 fixed point to .8 fixedpoint (hack only works with positve numbers, which it is) - t = (t + 0x8000) >> 16; - - // go from .8 fixed point to 8bitscale. ie t = (t*255)/256; - if(t >= 128) - return t - 1; - return t; - } - return(0); -} - -double KisCubicFilterStrategy::valueAt(double x) const { - if (x < -2.0) - return(0.0); - if (x < -1.0) - return((2.0+x)*(2.0+x)*(2.0+x)/6.0); - if (x < 0.0) - return((4.0+x*x*(-6.0-3.0*x))/6.0); - if (x < 1.0) - return((4.0+x*x*(-6.0+3.0*x))/6.0); - if (x < 2.0) - return((2.0-x)*(2.0-x)*(2.0-x)/6.0); - return(0.0); -} - -TQ_UINT32 KisCubicFilterStrategy::intValueAt(TQ_INT32 x) const { - if (x < 2) - return 0; - if (x < -1) - return (2 + x) * (2 + x) * ( 2 + x) / 6; - if ( x < 0) - return (4 + x * x * ( -6 - 3 * x)) / 6; - if (x < 1) - return (4 + x * x * ( -6 + 3 * x)) / 6; - if (x < 2) - return (2 - x) * ( 2 - x) * (2 - x) / 6; - return 0; -} - -double KisBoxFilterStrategy::valueAt(double t) const { - if((t > -0.5) && (t <= 0.5)) return(1.0); - return(0.0); -} - -TQ_UINT32 KisBoxFilterStrategy::intValueAt(TQ_INT32 t) const { - /* f(t) = 1, -0.5 < t <= 0.5 */ - if((t > -128) && (t <= 128)) - return 255; - return 0; -} - -double KisTriangleFilterStrategy::valueAt(double t) const { - if(t < 0.0) t = -t; - if(t < 1.0) return(1.0 - t); - return(0.0); -} - -TQ_UINT32 KisTriangleFilterStrategy::intValueAt(TQ_INT32 t) const { - /* f(t) = |t|, -1 <= t <= 1 */ - if(t < 0) t = -t; - if(t < 256) - { - // calc 256-1 but also go from .8 fixed point to 8bitscale. ie t = (t*255)/256; ie: if(t>=128) return t-1; - if(t>=128) return 256 - t; - return 255 - t; - } - return(0); -} - - -double KisBellFilterStrategy::valueAt(double t) const { - if(t < 0) t = -t; - if(t < .5) return(.75 - (t * t)); - if(t < 1.5) { - t = (t - 1.5); - return(.5 * (t * t)); - } - return(0.0); -} - -double KisBSplineFilterStrategy::valueAt(double t) const { - double tt; - - if(t < 0) t = -t; - if(t < 1) { - tt = t * t; - return((.5 * tt * t) - tt + (2.0 / 3.0)); - } else if(t < 2) { - t = 2 - t; - return((1.0 / 6.0) * (t * t * t)); - } - return(0.0); -} - -double KisLanczos3FilterStrategy::valueAt(double t) const { - if(t < 0) t = -t; - if(t < 3.0) return(sinc(t) * sinc(t/3.0)); - return(0.0); -} - -double KisLanczos3FilterStrategy::sinc(double x) const { - const double pi=3.1415926535897932385; - x *= pi; - if(x != 0) return(sin(x) / x); - return(1.0); -} - -double KisMitchellFilterStrategy::valueAt(double t) const { - const double B=1.0/3.0; - const double C=1.0/3.0; - double tt; - - tt = t * t; - if(t < 0) t = -t; - if(t < 1.0) { - t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt)) + ((-18.0 + 12.0 * B + 6.0 * C) * tt) + (6.0 - 2 * B)); - return(t / 6.0); - } else if(t < 2.0) { - t = (((-1.0 * B - 6.0 * C) * (t * tt)) + ((6.0 * B + 30.0 * C) * tt) + ((-12.0 * B - 48.0 * C) * t) + (8.0 * B + 24 * C)); - return(t / 6.0); - } - return(0.0); -} - -KisFilterStrategyRegistry *KisFilterStrategyRegistry::m_singleton = 0; - -KisFilterStrategyRegistry::KisFilterStrategyRegistry() -{ - Q_ASSERT(KisFilterStrategyRegistry::m_singleton == 0); - KisFilterStrategyRegistry::m_singleton = this; -} - -KisFilterStrategyRegistry::~KisFilterStrategyRegistry() -{ -} - -KisFilterStrategyRegistry* KisFilterStrategyRegistry::instance() -{ - if(KisFilterStrategyRegistry::m_singleton == 0) - { - KisFilterStrategyRegistry::m_singleton = new KisFilterStrategyRegistry(); - TQ_CHECK_PTR(KisFilterStrategyRegistry::m_singleton); - m_singleton->add(new KisHermiteFilterStrategy); - m_singleton->add(new KisBoxFilterStrategy); - m_singleton->add(new KisTriangleFilterStrategy); - m_singleton->add(new KisBellFilterStrategy); - m_singleton->add(new KisBSplineFilterStrategy); -// m_singleton->add(new KisLanczos3FilterStrategy); - m_singleton->add(new KisMitchellFilterStrategy); -// m_singleton->add(new KisCubicFilterStrategy); - } - return KisFilterStrategyRegistry::m_singleton; -} - diff --git a/chalk/core/kis_filter_strategy.cpp b/chalk/core/kis_filter_strategy.cpp new file mode 100644 index 00000000..650a9681 --- /dev/null +++ b/chalk/core/kis_filter_strategy.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2004 Michael Thaler + * Copyright (c) 2005 Casper Boemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include "kis_debug_areas.h" +#include "kis_filter_strategy.h" +#include + +double KisHermiteFilterStrategy::valueAt(double t) const { + /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ + if(t < 0.0) t = -t; + if(t < 1.0) return((2.0 * t - 3.0) * t * t + 1.0); + return(0.0); +} + +TQ_UINT32 KisHermiteFilterStrategy::intValueAt(TQ_INT32 t) const { + /* f(t) = 2|t|^3 - 3|t|^2 + 1, -1 <= t <= 1 */ + if(t < 0) t = -t; + if(t < 256) + { + t =(2 * t - 3*256) * t * t +(256<<16); + + //go from .24 fixed point to .8 fixedpoint (hack only works with positve numbers, which it is) + t = (t + 0x8000) >> 16; + + // go from .8 fixed point to 8bitscale. ie t = (t*255)/256; + if(t >= 128) + return t - 1; + return t; + } + return(0); +} + +double KisCubicFilterStrategy::valueAt(double x) const { + if (x < -2.0) + return(0.0); + if (x < -1.0) + return((2.0+x)*(2.0+x)*(2.0+x)/6.0); + if (x < 0.0) + return((4.0+x*x*(-6.0-3.0*x))/6.0); + if (x < 1.0) + return((4.0+x*x*(-6.0+3.0*x))/6.0); + if (x < 2.0) + return((2.0-x)*(2.0-x)*(2.0-x)/6.0); + return(0.0); +} + +TQ_UINT32 KisCubicFilterStrategy::intValueAt(TQ_INT32 x) const { + if (x < 2) + return 0; + if (x < -1) + return (2 + x) * (2 + x) * ( 2 + x) / 6; + if ( x < 0) + return (4 + x * x * ( -6 - 3 * x)) / 6; + if (x < 1) + return (4 + x * x * ( -6 + 3 * x)) / 6; + if (x < 2) + return (2 - x) * ( 2 - x) * (2 - x) / 6; + return 0; +} + +double KisBoxFilterStrategy::valueAt(double t) const { + if((t > -0.5) && (t <= 0.5)) return(1.0); + return(0.0); +} + +TQ_UINT32 KisBoxFilterStrategy::intValueAt(TQ_INT32 t) const { + /* f(t) = 1, -0.5 < t <= 0.5 */ + if((t > -128) && (t <= 128)) + return 255; + return 0; +} + +double KisTriangleFilterStrategy::valueAt(double t) const { + if(t < 0.0) t = -t; + if(t < 1.0) return(1.0 - t); + return(0.0); +} + +TQ_UINT32 KisTriangleFilterStrategy::intValueAt(TQ_INT32 t) const { + /* f(t) = |t|, -1 <= t <= 1 */ + if(t < 0) t = -t; + if(t < 256) + { + // calc 256-1 but also go from .8 fixed point to 8bitscale. ie t = (t*255)/256; ie: if(t>=128) return t-1; + if(t>=128) return 256 - t; + return 255 - t; + } + return(0); +} + + +double KisBellFilterStrategy::valueAt(double t) const { + if(t < 0) t = -t; + if(t < .5) return(.75 - (t * t)); + if(t < 1.5) { + t = (t - 1.5); + return(.5 * (t * t)); + } + return(0.0); +} + +double KisBSplineFilterStrategy::valueAt(double t) const { + double tt; + + if(t < 0) t = -t; + if(t < 1) { + tt = t * t; + return((.5 * tt * t) - tt + (2.0 / 3.0)); + } else if(t < 2) { + t = 2 - t; + return((1.0 / 6.0) * (t * t * t)); + } + return(0.0); +} + +double KisLanczos3FilterStrategy::valueAt(double t) const { + if(t < 0) t = -t; + if(t < 3.0) return(sinc(t) * sinc(t/3.0)); + return(0.0); +} + +double KisLanczos3FilterStrategy::sinc(double x) const { + const double pi=3.1415926535897932385; + x *= pi; + if(x != 0) return(sin(x) / x); + return(1.0); +} + +double KisMitchellFilterStrategy::valueAt(double t) const { + const double B=1.0/3.0; + const double C=1.0/3.0; + double tt; + + tt = t * t; + if(t < 0) t = -t; + if(t < 1.0) { + t = (((12.0 - 9.0 * B - 6.0 * C) * (t * tt)) + ((-18.0 + 12.0 * B + 6.0 * C) * tt) + (6.0 - 2 * B)); + return(t / 6.0); + } else if(t < 2.0) { + t = (((-1.0 * B - 6.0 * C) * (t * tt)) + ((6.0 * B + 30.0 * C) * tt) + ((-12.0 * B - 48.0 * C) * t) + (8.0 * B + 24 * C)); + return(t / 6.0); + } + return(0.0); +} + +KisFilterStrategyRegistry *KisFilterStrategyRegistry::m_singleton = 0; + +KisFilterStrategyRegistry::KisFilterStrategyRegistry() +{ + Q_ASSERT(KisFilterStrategyRegistry::m_singleton == 0); + KisFilterStrategyRegistry::m_singleton = this; +} + +KisFilterStrategyRegistry::~KisFilterStrategyRegistry() +{ +} + +KisFilterStrategyRegistry* KisFilterStrategyRegistry::instance() +{ + if(KisFilterStrategyRegistry::m_singleton == 0) + { + KisFilterStrategyRegistry::m_singleton = new KisFilterStrategyRegistry(); + TQ_CHECK_PTR(KisFilterStrategyRegistry::m_singleton); + m_singleton->add(new KisHermiteFilterStrategy); + m_singleton->add(new KisBoxFilterStrategy); + m_singleton->add(new KisTriangleFilterStrategy); + m_singleton->add(new KisBellFilterStrategy); + m_singleton->add(new KisBSplineFilterStrategy); +// m_singleton->add(new KisLanczos3FilterStrategy); + m_singleton->add(new KisMitchellFilterStrategy); +// m_singleton->add(new KisCubicFilterStrategy); + } + return KisFilterStrategyRegistry::m_singleton; +} + diff --git a/chalk/core/kis_gradient.cc b/chalk/core/kis_gradient.cc deleted file mode 100644 index 41541e80..00000000 --- a/chalk/core/kis_gradient.cc +++ /dev/null @@ -1,639 +0,0 @@ -/* - * kis_gradient.cc - part of Krayon - * - * Copyright (c) 2000 Matthias Elter - * 2001 John Califf - * 2004 Boudewijn Rempt - * 2004 Adrian Page - * 2004 Sven Langkamp - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include - -#include -#include -#include - -#include -#include - -#include -#include - -#include "kis_gradient.h" - -#define PREVIEW_WIDTH 64 -#define PREVIEW_HEIGHT 64 - -KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::m_instance = 0; -KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0; -KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0; - -KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::m_instance = 0; -KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::m_instance = 0; -KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::m_instance = 0; -KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0; -KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0; - -KisGradient::KisGradient(const TQString& file) : super(file) -{ -} - -KisGradient::~KisGradient() -{ - for (uint i = 0; i < m_segments.count(); i++) { - delete m_segments[i]; - m_segments[i] = 0; - } -} - -bool KisGradient::load() -{ - return init(); -} - -bool KisGradient::save() -{ - return false; -} - -TQImage KisGradient::img() -{ - return m_img; -} - -bool KisGradient::init() -{ - KoGradientManager gradLoader; - KoGradient* grad = gradLoader.loadGradient(filename()); - - if( !grad ) - return false; - - m_segments.clear(); - - if( grad->colorStops.count() > 1 ) { - KoColorStop *colstop; - for(colstop = grad->colorStops.first(); colstop; colstop = grad->colorStops.next()) { - KoColorStop *colstopNext = grad->colorStops.next(); - - if(colstopNext) { - KoColor leftRgb((int)(colstop->color1 * 255 + 0.5), (int)(colstop->color2 * 255 + 0.5), (int)(colstop->color3 * 255 + 0.5)); - KoColor rightRgb((int)(colstopNext->color1 * 255 + 0.5), (int)(colstopNext->color2 * 255 + 0.5), (int)(colstopNext->color3 * 255 + 0.5)); - - double midp = colstop->midpoint; - midp = colstop->offset + ((colstopNext->offset - colstop->offset) * midp); - - Color leftColor(leftRgb.color(), colstop->opacity); - Color rightColor(rightRgb.color(), colstopNext->opacity); - - KisGradientSegment *segment = new KisGradientSegment(colstop->interpolation, colstop->colorType, colstop->offset, midp, colstopNext->offset, leftColor, rightColor); - TQ_CHECK_PTR(segment); - - if ( !segment->isValid() ) { - delete segment; - return false; - } - - m_segments.push_back(segment); - grad->colorStops.prev(); - } - else { - grad->colorStops.prev(); - break; - } - } - } - else - return false; - - if (!m_segments.isEmpty()) { - m_img = generatePreview(PREVIEW_WIDTH, PREVIEW_HEIGHT); - setValid(true); - return true; - } - else { - return false; - } -} - -void KisGradient::setImage(const TQImage& img) -{ - m_img = img; - m_img.detach(); - - setValid(true); -} - -KisGradientSegment *KisGradient::segmentAt(double t) const -{ - Q_ASSERT(t >= 0 || t <= 1); - Q_ASSERT(!m_segments.empty()); - - for(TQValueVector::const_iterator it = m_segments.begin(); it!= m_segments.end(); ++it) - { - if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) { - return *it; - } - } - - return 0; -} - -void KisGradient::colorAt(double t, TQColor *color, TQ_UINT8 *opacity) const -{ - const KisGradientSegment *segment = segmentAt(t); - Q_ASSERT(segment != 0); - - if (segment) { - Color col = segment->colorAt(t); - *color = col.color(); - *opacity = static_cast(col.alpha() * OPACITY_OPAQUE + 0.5); - } -} - -TQImage KisGradient::generatePreview(int width, int height) const -{ - TQImage img(width, height, 32); - - for (int y = 0; y < img.height(); y++) { - for (int x = 0; x < img.width(); x++) { - - int backgroundRed = 128 + 63 * ((x / 4 + y / 4) % 2); - int backgroundGreen = backgroundRed; - int backgroundBlue = backgroundRed; - - TQColor color; - TQ_UINT8 opacity; - double t = static_cast(x) / (img.width() - 1); - - colorAt(t, &color, &opacity); - - double alpha = static_cast(opacity) / OPACITY_OPAQUE; - - int red = static_cast((1 - alpha) * backgroundRed + alpha * color.red() + 0.5); - int green = static_cast((1 - alpha) * backgroundGreen + alpha * color.green() + 0.5); - int blue = static_cast((1 - alpha) * backgroundBlue + alpha * color.blue() + 0.5); - - img.setPixel(x, y, tqRgb(red, green, blue)); - } - } - - return img; -} - -KisGradientSegment::KisGradientSegment(int interpolationType, int colorInterpolationType, double startOffset, double middleOffset, double endOffset, const Color& startColor, const Color& endColor) -{ - m_interpolator = 0; - - switch (interpolationType) { - case INTERP_LINEAR: - m_interpolator = LinearInterpolationStrategy::instance(); - break; - case INTERP_CURVED: - m_interpolator = CurvedInterpolationStrategy::instance(); - break; - case INTERP_SINE: - m_interpolator = SineInterpolationStrategy::instance(); - break; - case INTERP_SPHERE_INCREASING: - m_interpolator = SphereIncreasingInterpolationStrategy::instance(); - break; - case INTERP_SPHERE_DECREASING: - m_interpolator = SphereDecreasingInterpolationStrategy::instance(); - break; - } - - m_colorInterpolator = 0; - - switch (colorInterpolationType) { - case COLOR_INTERP_RGB: - m_colorInterpolator = RGBColorInterpolationStrategy::instance(); - break; - case COLOR_INTERP_HSV_CCW: - m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); - break; - case COLOR_INTERP_HSV_CW: - m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); - break; - } - - if (startOffset < DBL_EPSILON) { - m_startOffset = 0; - } - else - if (startOffset > 1 - DBL_EPSILON) { - m_startOffset = 1; - } - else { - m_startOffset = startOffset; - } - - if (middleOffset < m_startOffset + DBL_EPSILON) { - m_middleOffset = m_startOffset; - } - else - if (middleOffset > 1 - DBL_EPSILON) { - m_middleOffset = 1; - } - else { - m_middleOffset = middleOffset; - } - - if (endOffset < m_middleOffset + DBL_EPSILON) { - m_endOffset = m_middleOffset; - } - else - if (endOffset > 1 - DBL_EPSILON) { - m_endOffset = 1; - } - else { - m_endOffset = endOffset; - } - - m_length = m_endOffset - m_startOffset; - - if (m_length < DBL_EPSILON) { - m_middleT = 0.5; - } - else { - m_middleT = (m_middleOffset - m_startOffset) / m_length; - } - - m_startColor = startColor; - m_endColor = endColor; -} - -const Color& KisGradientSegment::startColor() const -{ - return m_startColor; -} - -const Color& KisGradientSegment::endColor() const -{ - return m_endColor; -} - -double KisGradientSegment::startOffset() const -{ - return m_startOffset; -} - -double KisGradientSegment::middleOffset() const -{ - return m_middleOffset; -} - -double KisGradientSegment::endOffset() const -{ - return m_endOffset; -} - -void KisGradientSegment::setStartOffset(double t) -{ - m_startOffset = t; - m_length = m_endOffset - m_startOffset; - - if (m_length < DBL_EPSILON) { - m_middleT = 0.5; - } - else { - m_middleT = (m_middleOffset - m_startOffset) / m_length; - } -} -void KisGradientSegment::setMiddleOffset(double t) -{ - m_middleOffset = t; - - if (m_length < DBL_EPSILON) { - m_middleT = 0.5; - } - else { - m_middleT = (m_middleOffset - m_startOffset) / m_length; - } -} - -void KisGradientSegment::setEndOffset(double t) -{ - m_endOffset = t; - m_length = m_endOffset - m_startOffset; - - if (m_length < DBL_EPSILON) { - m_middleT = 0.5; - } - else { - m_middleT = (m_middleOffset - m_startOffset) / m_length; - } -} - -int KisGradientSegment::interpolation() const -{ - return m_interpolator->type(); -} - -void KisGradientSegment::setInterpolation(int interpolationType) -{ - switch (interpolationType) { - case INTERP_LINEAR: - m_interpolator = LinearInterpolationStrategy::instance(); - break; - case INTERP_CURVED: - m_interpolator = CurvedInterpolationStrategy::instance(); - break; - case INTERP_SINE: - m_interpolator = SineInterpolationStrategy::instance(); - break; - case INTERP_SPHERE_INCREASING: - m_interpolator = SphereIncreasingInterpolationStrategy::instance(); - break; - case INTERP_SPHERE_DECREASING: - m_interpolator = SphereDecreasingInterpolationStrategy::instance(); - break; - } -} - -int KisGradientSegment::colorInterpolation() const -{ - return m_colorInterpolator->type(); -} - -void KisGradientSegment::setColorInterpolation(int colorInterpolationType) -{ - switch (colorInterpolationType) { - case COLOR_INTERP_RGB: - m_colorInterpolator = RGBColorInterpolationStrategy::instance(); - break; - case COLOR_INTERP_HSV_CCW: - m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); - break; - case COLOR_INTERP_HSV_CW: - m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); - break; - } -} - -Color KisGradientSegment::colorAt(double t) const -{ - Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON); - - double segmentT; - - if (m_length < DBL_EPSILON) { - segmentT = 0.5; - } - else { - segmentT = (t - m_startOffset) / m_length; - } - - double colorT = m_interpolator->valueAt(segmentT, m_middleT); - - Color color = m_colorInterpolator->colorAt(colorT, m_startColor, m_endColor); - - return color; -} - -bool KisGradientSegment::isValid() const -{ - if (m_interpolator == 0 || m_colorInterpolator ==0) - return false; - return true; -} - -KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::instance() -{ - if (m_instance == 0) { - m_instance = new RGBColorInterpolationStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; -} - -Color KisGradientSegment::RGBColorInterpolationStrategy::colorAt(double t, Color start, Color end) const -{ - int startRed = start.color().red(); - int startGreen = start.color().green(); - int startBlue = start.color().blue(); - double startAlpha = start.alpha(); - int red = static_cast(startRed + t * (end.color().red() - startRed) + 0.5); - int green = static_cast(startGreen + t * (end.color().green() - startGreen) + 0.5); - int blue = static_cast(startBlue + t * (end.color().blue() - startBlue) + 0.5); - double alpha = startAlpha + t * (end.alpha() - startAlpha); - - return Color(TQColor(red, green, blue), alpha); -} - -KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::instance() -{ - if (m_instance == 0) { - m_instance = new HSVCWColorInterpolationStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; -} - -Color KisGradientSegment::HSVCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const -{ - KoColor sc = KoColor(start.color()); - KoColor ec = KoColor(end.color()); - - int s = static_cast(sc.S() + t * (ec.S() - sc.S()) + 0.5); - int v = static_cast(sc.V() + t * (ec.V() - sc.V()) + 0.5); - int h; - - if (ec.H() < sc.H()) { - h = static_cast(ec.H() + (1 - t) * (sc.H() - ec.H()) + 0.5); - } - else { - h = static_cast(ec.H() + (1 - t) * (360 - ec.H() + sc.H()) + 0.5); - - if (h > 359) { - h -= 360; - } - } - - double alpha = start.alpha() + t * (end.alpha() - start.alpha()); - - return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha); -} - -KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::instance() -{ - if (m_instance == 0) { - m_instance = new HSVCCWColorInterpolationStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; -} - -Color KisGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const -{ - KoColor sc = KoColor(start.color()); - KoColor se = KoColor(end.color()); - - int s = static_cast(sc.S() + t * (se.S() - sc.S()) + 0.5); - int v = static_cast(sc.V() + t * (se.V() - sc.V()) + 0.5); - int h; - - if (sc.H() < se.H()) { - h = static_cast(sc.H() + t * (se.H() - sc.H()) + 0.5); - } - else { - h = static_cast(sc.H() + t * (360 - sc.H() + se.H()) + 0.5); - - if (h > 359) { - h -= 360; - } - } - - double alpha = start.alpha() + t * (end.alpha() - start.alpha()); - - return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha); -} - -KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::instance() -{ - if (m_instance == 0) { - m_instance = new LinearInterpolationStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; -} - -double KisGradientSegment::LinearInterpolationStrategy::calcValueAt(double t, double middle) -{ - Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); - Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); - - double value = 0; - - if (t <= middle) { - if (middle < DBL_EPSILON) { - value = 0; - } - else { - value = (t / middle) * 0.5; - } - } - else { - if (middle > 1 - DBL_EPSILON) { - value = 1; - } - else { - value = ((t - middle) / (1 - middle)) * 0.5 + 0.5; - } - } - - return value; -} - -double KisGradientSegment::LinearInterpolationStrategy::valueAt(double t, double middle) const -{ - return calcValueAt(t, middle); -} - -KisGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy() -{ - m_logHalf = log(0.5); -} - -KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::instance() -{ - if (m_instance == 0) { - m_instance = new CurvedInterpolationStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; -} - -double KisGradientSegment::CurvedInterpolationStrategy::valueAt(double t, double middle) const -{ - Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); - Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); - - double value = 0; - - if (middle < DBL_EPSILON) { - middle = DBL_EPSILON; - } - - value = pow(t, m_logHalf / log(middle)); - - return value; -} - -KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::instance() -{ - if (m_instance == 0) { - m_instance = new SineInterpolationStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; -} - -double KisGradientSegment::SineInterpolationStrategy::valueAt(double t, double middle) const -{ - double lt = LinearInterpolationStrategy::calcValueAt(t, middle); - double value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0; - - return value; -} - -KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::instance() -{ - if (m_instance == 0) { - m_instance = new SphereIncreasingInterpolationStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; -} - -double KisGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(double t, double middle) const -{ - double lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1; - double value = sqrt(1 - lt * lt); - - return value; -} - -KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::instance() -{ - if (m_instance == 0) { - m_instance = new SphereDecreasingInterpolationStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; -} - -double KisGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(double t, double middle) const -{ - double lt = LinearInterpolationStrategy::calcValueAt(t, middle); - double value = 1 - sqrt(1 - lt * lt); - - return value; -} - -#include "kis_gradient.moc" - diff --git a/chalk/core/kis_gradient.cpp b/chalk/core/kis_gradient.cpp new file mode 100644 index 00000000..9b240cb0 --- /dev/null +++ b/chalk/core/kis_gradient.cpp @@ -0,0 +1,639 @@ +/* + * kis_gradient.cpp - part of Krayon + * + * Copyright (c) 2000 Matthias Elter + * 2001 John Califf + * 2004 Boudewijn Rempt + * 2004 Adrian Page + * 2004 Sven Langkamp + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include "kis_gradient.h" + +#define PREVIEW_WIDTH 64 +#define PREVIEW_HEIGHT 64 + +KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::m_instance = 0; +KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::m_instance = 0; +KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::m_instance = 0; + +KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::m_instance = 0; +KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::m_instance = 0; +KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::m_instance = 0; +KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::m_instance = 0; +KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::m_instance = 0; + +KisGradient::KisGradient(const TQString& file) : super(file) +{ +} + +KisGradient::~KisGradient() +{ + for (uint i = 0; i < m_segments.count(); i++) { + delete m_segments[i]; + m_segments[i] = 0; + } +} + +bool KisGradient::load() +{ + return init(); +} + +bool KisGradient::save() +{ + return false; +} + +TQImage KisGradient::img() +{ + return m_img; +} + +bool KisGradient::init() +{ + KoGradientManager gradLoader; + KoGradient* grad = gradLoader.loadGradient(filename()); + + if( !grad ) + return false; + + m_segments.clear(); + + if( grad->colorStops.count() > 1 ) { + KoColorStop *colstop; + for(colstop = grad->colorStops.first(); colstop; colstop = grad->colorStops.next()) { + KoColorStop *colstopNext = grad->colorStops.next(); + + if(colstopNext) { + KoColor leftRgb((int)(colstop->color1 * 255 + 0.5), (int)(colstop->color2 * 255 + 0.5), (int)(colstop->color3 * 255 + 0.5)); + KoColor rightRgb((int)(colstopNext->color1 * 255 + 0.5), (int)(colstopNext->color2 * 255 + 0.5), (int)(colstopNext->color3 * 255 + 0.5)); + + double midp = colstop->midpoint; + midp = colstop->offset + ((colstopNext->offset - colstop->offset) * midp); + + Color leftColor(leftRgb.color(), colstop->opacity); + Color rightColor(rightRgb.color(), colstopNext->opacity); + + KisGradientSegment *segment = new KisGradientSegment(colstop->interpolation, colstop->colorType, colstop->offset, midp, colstopNext->offset, leftColor, rightColor); + TQ_CHECK_PTR(segment); + + if ( !segment->isValid() ) { + delete segment; + return false; + } + + m_segments.push_back(segment); + grad->colorStops.prev(); + } + else { + grad->colorStops.prev(); + break; + } + } + } + else + return false; + + if (!m_segments.isEmpty()) { + m_img = generatePreview(PREVIEW_WIDTH, PREVIEW_HEIGHT); + setValid(true); + return true; + } + else { + return false; + } +} + +void KisGradient::setImage(const TQImage& img) +{ + m_img = img; + m_img.detach(); + + setValid(true); +} + +KisGradientSegment *KisGradient::segmentAt(double t) const +{ + Q_ASSERT(t >= 0 || t <= 1); + Q_ASSERT(!m_segments.empty()); + + for(TQValueVector::const_iterator it = m_segments.begin(); it!= m_segments.end(); ++it) + { + if (t > (*it)->startOffset() - DBL_EPSILON && t < (*it)->endOffset() + DBL_EPSILON) { + return *it; + } + } + + return 0; +} + +void KisGradient::colorAt(double t, TQColor *color, TQ_UINT8 *opacity) const +{ + const KisGradientSegment *segment = segmentAt(t); + Q_ASSERT(segment != 0); + + if (segment) { + Color col = segment->colorAt(t); + *color = col.color(); + *opacity = static_cast(col.alpha() * OPACITY_OPAQUE + 0.5); + } +} + +TQImage KisGradient::generatePreview(int width, int height) const +{ + TQImage img(width, height, 32); + + for (int y = 0; y < img.height(); y++) { + for (int x = 0; x < img.width(); x++) { + + int backgroundRed = 128 + 63 * ((x / 4 + y / 4) % 2); + int backgroundGreen = backgroundRed; + int backgroundBlue = backgroundRed; + + TQColor color; + TQ_UINT8 opacity; + double t = static_cast(x) / (img.width() - 1); + + colorAt(t, &color, &opacity); + + double alpha = static_cast(opacity) / OPACITY_OPAQUE; + + int red = static_cast((1 - alpha) * backgroundRed + alpha * color.red() + 0.5); + int green = static_cast((1 - alpha) * backgroundGreen + alpha * color.green() + 0.5); + int blue = static_cast((1 - alpha) * backgroundBlue + alpha * color.blue() + 0.5); + + img.setPixel(x, y, tqRgb(red, green, blue)); + } + } + + return img; +} + +KisGradientSegment::KisGradientSegment(int interpolationType, int colorInterpolationType, double startOffset, double middleOffset, double endOffset, const Color& startColor, const Color& endColor) +{ + m_interpolator = 0; + + switch (interpolationType) { + case INTERP_LINEAR: + m_interpolator = LinearInterpolationStrategy::instance(); + break; + case INTERP_CURVED: + m_interpolator = CurvedInterpolationStrategy::instance(); + break; + case INTERP_SINE: + m_interpolator = SineInterpolationStrategy::instance(); + break; + case INTERP_SPHERE_INCREASING: + m_interpolator = SphereIncreasingInterpolationStrategy::instance(); + break; + case INTERP_SPHERE_DECREASING: + m_interpolator = SphereDecreasingInterpolationStrategy::instance(); + break; + } + + m_colorInterpolator = 0; + + switch (colorInterpolationType) { + case COLOR_INTERP_RGB: + m_colorInterpolator = RGBColorInterpolationStrategy::instance(); + break; + case COLOR_INTERP_HSV_CCW: + m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); + break; + case COLOR_INTERP_HSV_CW: + m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); + break; + } + + if (startOffset < DBL_EPSILON) { + m_startOffset = 0; + } + else + if (startOffset > 1 - DBL_EPSILON) { + m_startOffset = 1; + } + else { + m_startOffset = startOffset; + } + + if (middleOffset < m_startOffset + DBL_EPSILON) { + m_middleOffset = m_startOffset; + } + else + if (middleOffset > 1 - DBL_EPSILON) { + m_middleOffset = 1; + } + else { + m_middleOffset = middleOffset; + } + + if (endOffset < m_middleOffset + DBL_EPSILON) { + m_endOffset = m_middleOffset; + } + else + if (endOffset > 1 - DBL_EPSILON) { + m_endOffset = 1; + } + else { + m_endOffset = endOffset; + } + + m_length = m_endOffset - m_startOffset; + + if (m_length < DBL_EPSILON) { + m_middleT = 0.5; + } + else { + m_middleT = (m_middleOffset - m_startOffset) / m_length; + } + + m_startColor = startColor; + m_endColor = endColor; +} + +const Color& KisGradientSegment::startColor() const +{ + return m_startColor; +} + +const Color& KisGradientSegment::endColor() const +{ + return m_endColor; +} + +double KisGradientSegment::startOffset() const +{ + return m_startOffset; +} + +double KisGradientSegment::middleOffset() const +{ + return m_middleOffset; +} + +double KisGradientSegment::endOffset() const +{ + return m_endOffset; +} + +void KisGradientSegment::setStartOffset(double t) +{ + m_startOffset = t; + m_length = m_endOffset - m_startOffset; + + if (m_length < DBL_EPSILON) { + m_middleT = 0.5; + } + else { + m_middleT = (m_middleOffset - m_startOffset) / m_length; + } +} +void KisGradientSegment::setMiddleOffset(double t) +{ + m_middleOffset = t; + + if (m_length < DBL_EPSILON) { + m_middleT = 0.5; + } + else { + m_middleT = (m_middleOffset - m_startOffset) / m_length; + } +} + +void KisGradientSegment::setEndOffset(double t) +{ + m_endOffset = t; + m_length = m_endOffset - m_startOffset; + + if (m_length < DBL_EPSILON) { + m_middleT = 0.5; + } + else { + m_middleT = (m_middleOffset - m_startOffset) / m_length; + } +} + +int KisGradientSegment::interpolation() const +{ + return m_interpolator->type(); +} + +void KisGradientSegment::setInterpolation(int interpolationType) +{ + switch (interpolationType) { + case INTERP_LINEAR: + m_interpolator = LinearInterpolationStrategy::instance(); + break; + case INTERP_CURVED: + m_interpolator = CurvedInterpolationStrategy::instance(); + break; + case INTERP_SINE: + m_interpolator = SineInterpolationStrategy::instance(); + break; + case INTERP_SPHERE_INCREASING: + m_interpolator = SphereIncreasingInterpolationStrategy::instance(); + break; + case INTERP_SPHERE_DECREASING: + m_interpolator = SphereDecreasingInterpolationStrategy::instance(); + break; + } +} + +int KisGradientSegment::colorInterpolation() const +{ + return m_colorInterpolator->type(); +} + +void KisGradientSegment::setColorInterpolation(int colorInterpolationType) +{ + switch (colorInterpolationType) { + case COLOR_INTERP_RGB: + m_colorInterpolator = RGBColorInterpolationStrategy::instance(); + break; + case COLOR_INTERP_HSV_CCW: + m_colorInterpolator = HSVCCWColorInterpolationStrategy::instance(); + break; + case COLOR_INTERP_HSV_CW: + m_colorInterpolator = HSVCWColorInterpolationStrategy::instance(); + break; + } +} + +Color KisGradientSegment::colorAt(double t) const +{ + Q_ASSERT(t > m_startOffset - DBL_EPSILON && t < m_endOffset + DBL_EPSILON); + + double segmentT; + + if (m_length < DBL_EPSILON) { + segmentT = 0.5; + } + else { + segmentT = (t - m_startOffset) / m_length; + } + + double colorT = m_interpolator->valueAt(segmentT, m_middleT); + + Color color = m_colorInterpolator->colorAt(colorT, m_startColor, m_endColor); + + return color; +} + +bool KisGradientSegment::isValid() const +{ + if (m_interpolator == 0 || m_colorInterpolator ==0) + return false; + return true; +} + +KisGradientSegment::RGBColorInterpolationStrategy *KisGradientSegment::RGBColorInterpolationStrategy::instance() +{ + if (m_instance == 0) { + m_instance = new RGBColorInterpolationStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; +} + +Color KisGradientSegment::RGBColorInterpolationStrategy::colorAt(double t, Color start, Color end) const +{ + int startRed = start.color().red(); + int startGreen = start.color().green(); + int startBlue = start.color().blue(); + double startAlpha = start.alpha(); + int red = static_cast(startRed + t * (end.color().red() - startRed) + 0.5); + int green = static_cast(startGreen + t * (end.color().green() - startGreen) + 0.5); + int blue = static_cast(startBlue + t * (end.color().blue() - startBlue) + 0.5); + double alpha = startAlpha + t * (end.alpha() - startAlpha); + + return Color(TQColor(red, green, blue), alpha); +} + +KisGradientSegment::HSVCWColorInterpolationStrategy *KisGradientSegment::HSVCWColorInterpolationStrategy::instance() +{ + if (m_instance == 0) { + m_instance = new HSVCWColorInterpolationStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; +} + +Color KisGradientSegment::HSVCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const +{ + KoColor sc = KoColor(start.color()); + KoColor ec = KoColor(end.color()); + + int s = static_cast(sc.S() + t * (ec.S() - sc.S()) + 0.5); + int v = static_cast(sc.V() + t * (ec.V() - sc.V()) + 0.5); + int h; + + if (ec.H() < sc.H()) { + h = static_cast(ec.H() + (1 - t) * (sc.H() - ec.H()) + 0.5); + } + else { + h = static_cast(ec.H() + (1 - t) * (360 - ec.H() + sc.H()) + 0.5); + + if (h > 359) { + h -= 360; + } + } + + double alpha = start.alpha() + t * (end.alpha() - start.alpha()); + + return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha); +} + +KisGradientSegment::HSVCCWColorInterpolationStrategy *KisGradientSegment::HSVCCWColorInterpolationStrategy::instance() +{ + if (m_instance == 0) { + m_instance = new HSVCCWColorInterpolationStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; +} + +Color KisGradientSegment::HSVCCWColorInterpolationStrategy::colorAt(double t, Color start, Color end) const +{ + KoColor sc = KoColor(start.color()); + KoColor se = KoColor(end.color()); + + int s = static_cast(sc.S() + t * (se.S() - sc.S()) + 0.5); + int v = static_cast(sc.V() + t * (se.V() - sc.V()) + 0.5); + int h; + + if (sc.H() < se.H()) { + h = static_cast(sc.H() + t * (se.H() - sc.H()) + 0.5); + } + else { + h = static_cast(sc.H() + t * (360 - sc.H() + se.H()) + 0.5); + + if (h > 359) { + h -= 360; + } + } + + double alpha = start.alpha() + t * (end.alpha() - start.alpha()); + + return Color(KoColor(h, s, v, KoColor::csHSV).color(), alpha); +} + +KisGradientSegment::LinearInterpolationStrategy *KisGradientSegment::LinearInterpolationStrategy::instance() +{ + if (m_instance == 0) { + m_instance = new LinearInterpolationStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; +} + +double KisGradientSegment::LinearInterpolationStrategy::calcValueAt(double t, double middle) +{ + Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); + Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); + + double value = 0; + + if (t <= middle) { + if (middle < DBL_EPSILON) { + value = 0; + } + else { + value = (t / middle) * 0.5; + } + } + else { + if (middle > 1 - DBL_EPSILON) { + value = 1; + } + else { + value = ((t - middle) / (1 - middle)) * 0.5 + 0.5; + } + } + + return value; +} + +double KisGradientSegment::LinearInterpolationStrategy::valueAt(double t, double middle) const +{ + return calcValueAt(t, middle); +} + +KisGradientSegment::CurvedInterpolationStrategy::CurvedInterpolationStrategy() +{ + m_logHalf = log(0.5); +} + +KisGradientSegment::CurvedInterpolationStrategy *KisGradientSegment::CurvedInterpolationStrategy::instance() +{ + if (m_instance == 0) { + m_instance = new CurvedInterpolationStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; +} + +double KisGradientSegment::CurvedInterpolationStrategy::valueAt(double t, double middle) const +{ + Q_ASSERT(t > -DBL_EPSILON && t < 1 + DBL_EPSILON); + Q_ASSERT(middle > -DBL_EPSILON && middle < 1 + DBL_EPSILON); + + double value = 0; + + if (middle < DBL_EPSILON) { + middle = DBL_EPSILON; + } + + value = pow(t, m_logHalf / log(middle)); + + return value; +} + +KisGradientSegment::SineInterpolationStrategy *KisGradientSegment::SineInterpolationStrategy::instance() +{ + if (m_instance == 0) { + m_instance = new SineInterpolationStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; +} + +double KisGradientSegment::SineInterpolationStrategy::valueAt(double t, double middle) const +{ + double lt = LinearInterpolationStrategy::calcValueAt(t, middle); + double value = (sin(-M_PI_2 + M_PI * lt) + 1.0) / 2.0; + + return value; +} + +KisGradientSegment::SphereIncreasingInterpolationStrategy *KisGradientSegment::SphereIncreasingInterpolationStrategy::instance() +{ + if (m_instance == 0) { + m_instance = new SphereIncreasingInterpolationStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; +} + +double KisGradientSegment::SphereIncreasingInterpolationStrategy::valueAt(double t, double middle) const +{ + double lt = LinearInterpolationStrategy::calcValueAt(t, middle) - 1; + double value = sqrt(1 - lt * lt); + + return value; +} + +KisGradientSegment::SphereDecreasingInterpolationStrategy *KisGradientSegment::SphereDecreasingInterpolationStrategy::instance() +{ + if (m_instance == 0) { + m_instance = new SphereDecreasingInterpolationStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; +} + +double KisGradientSegment::SphereDecreasingInterpolationStrategy::valueAt(double t, double middle) const +{ + double lt = LinearInterpolationStrategy::calcValueAt(t, middle); + double value = 1 - sqrt(1 - lt * lt); + + return value; +} + +#include "kis_gradient.moc" + diff --git a/chalk/core/kis_gradient_painter.cc b/chalk/core/kis_gradient_painter.cc deleted file mode 100644 index 7c2b00b1..00000000 --- a/chalk/core/kis_gradient_painter.cc +++ /dev/null @@ -1,723 +0,0 @@ -/* - * Copyright (c) 2004 Adrian Page - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include - -#include "tqbrush.h" -#include "tqcolor.h" -#include "tqfontinfo.h" -#include "tqfontmetrics.h" -#include "tqpen.h" -#include "tqregion.h" -#include "tqwmatrix.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "kis_brush.h" -#include "kis_debug_areas.h" -#include "kis_gradient.h" -#include "kis_image.h" -#include "kis_iterators_pixel.h" -#include "kis_layer.h" -#include "kis_paint_device.h" -#include "kis_pattern.h" -#include "kis_rect.h" -#include "kis_colorspace.h" -#include "kis_types.h" -#include "kis_vec.h" -#include "kis_selection.h" -#include "kis_gradient_painter.h" -#include "kis_meta_registry.h" -#include "kis_colorspace_factory_registry.h" - -namespace { - - class GradientShapeStrategy { - public: - GradientShapeStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); - virtual ~GradientShapeStrategy() {} - - virtual double valueAt(double x, double y) const = 0; - - protected: - KisPoint m_gradientVectorStart; - KisPoint m_gradientVectorEnd; - }; - - GradientShapeStrategy::GradientShapeStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) - : m_gradientVectorStart(gradientVectorStart), m_gradientVectorEnd(gradientVectorEnd) - { - } - - - class LinearGradientStrategy : public GradientShapeStrategy { - typedef GradientShapeStrategy super; - public: - LinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); - - virtual double valueAt(double x, double y) const; - - protected: - double m_normalisedVectorX; - double m_normalisedVectorY; - double m_vectorLength; - }; - - LinearGradientStrategy::LinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) - : super(gradientVectorStart, gradientVectorEnd) - { - double dx = gradientVectorEnd.x() - gradientVectorStart.x(); - double dy = gradientVectorEnd.y() - gradientVectorStart.y(); - - m_vectorLength = sqrt((dx * dx) + (dy * dy)); - - if (m_vectorLength < DBL_EPSILON) { - m_normalisedVectorX = 0; - m_normalisedVectorY = 0; - } - else { - m_normalisedVectorX = dx / m_vectorLength; - m_normalisedVectorY = dy / m_vectorLength; - } - } - - double LinearGradientStrategy::valueAt(double x, double y) const - { - double vx = x - m_gradientVectorStart.x(); - double vy = y - m_gradientVectorStart.y(); - - // Project the vector onto the normalised gradient vector. - double t = vx * m_normalisedVectorX + vy * m_normalisedVectorY; - - if (m_vectorLength < DBL_EPSILON) { - t = 0; - } - else { - // Scale to 0 to 1 over the gradient vector length. - t /= m_vectorLength; - } - - return t; - } - - - class BiLinearGradientStrategy : public LinearGradientStrategy { - typedef LinearGradientStrategy super; - public: - BiLinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); - - virtual double valueAt(double x, double y) const; - }; - - BiLinearGradientStrategy::BiLinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) - : super(gradientVectorStart, gradientVectorEnd) - { - } - - double BiLinearGradientStrategy::valueAt(double x, double y) const - { - double t = super::valueAt(x, y); - - // Reflect - if (t < -DBL_EPSILON) { - t = -t; - } - - return t; - } - - - class RadialGradientStrategy : public GradientShapeStrategy { - typedef GradientShapeStrategy super; - public: - RadialGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); - - virtual double valueAt(double x, double y) const; - - protected: - double m_radius; - }; - - RadialGradientStrategy::RadialGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) - : super(gradientVectorStart, gradientVectorEnd) - { - double dx = gradientVectorEnd.x() - gradientVectorStart.x(); - double dy = gradientVectorEnd.y() - gradientVectorStart.y(); - - m_radius = sqrt((dx * dx) + (dy * dy)); - } - - double RadialGradientStrategy::valueAt(double x, double y) const - { - double dx = x - m_gradientVectorStart.x(); - double dy = y - m_gradientVectorStart.y(); - - double distance = sqrt((dx * dx) + (dy * dy)); - - double t; - - if (m_radius < DBL_EPSILON) { - t = 0; - } - else { - t = distance / m_radius; - } - - return t; - } - - - class SquareGradientStrategy : public GradientShapeStrategy { - typedef GradientShapeStrategy super; - public: - SquareGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); - - virtual double valueAt(double x, double y) const; - - protected: - double m_normalisedVectorX; - double m_normalisedVectorY; - double m_vectorLength; - }; - - SquareGradientStrategy::SquareGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) - : super(gradientVectorStart, gradientVectorEnd) - { - double dx = gradientVectorEnd.x() - gradientVectorStart.x(); - double dy = gradientVectorEnd.y() - gradientVectorStart.y(); - - m_vectorLength = sqrt((dx * dx) + (dy * dy)); - - if (m_vectorLength < DBL_EPSILON) { - m_normalisedVectorX = 0; - m_normalisedVectorY = 0; - } - else { - m_normalisedVectorX = dx / m_vectorLength; - m_normalisedVectorY = dy / m_vectorLength; - } - } - - double SquareGradientStrategy::valueAt(double x, double y) const - { - double px = x - m_gradientVectorStart.x(); - double py = y - m_gradientVectorStart.y(); - - double distance1 = 0; - double distance2 = 0; - - if (m_vectorLength > DBL_EPSILON) { - - // Point to line distance is: - // distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / m_vectorLength; - // - // Here l0 = (0, 0) and |l1 - l0| = 1 - - distance1 = -m_normalisedVectorY * px + m_normalisedVectorX * py; - distance1 = fabs(distance1); - - // Rotate point by 90 degrees and get the distance to the perpendicular - distance2 = -m_normalisedVectorY * -py + m_normalisedVectorX * px; - distance2 = fabs(distance2); - } - - double t = TQMAX(distance1, distance2) / m_vectorLength; - - return t; - } - - - class ConicalGradientStrategy : public GradientShapeStrategy { - typedef GradientShapeStrategy super; - public: - ConicalGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); - - virtual double valueAt(double x, double y) const; - - protected: - double m_vectorAngle; - }; - - ConicalGradientStrategy::ConicalGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) - : super(gradientVectorStart, gradientVectorEnd) - { - double dx = gradientVectorEnd.x() - gradientVectorStart.x(); - double dy = gradientVectorEnd.y() - gradientVectorStart.y(); - - // Get angle from 0 to 2 PI. - m_vectorAngle = atan2(dy, dx) + M_PI; - } - - double ConicalGradientStrategy::valueAt(double x, double y) const - { - double px = x - m_gradientVectorStart.x(); - double py = y - m_gradientVectorStart.y(); - - double angle = atan2(py, px) + M_PI; - - angle -= m_vectorAngle; - - if (angle < 0) { - angle += 2 * M_PI; - } - - double t = angle / (2 * M_PI); - - return t; - } - - - class ConicalSymetricGradientStrategy : public GradientShapeStrategy { - typedef GradientShapeStrategy super; - public: - ConicalSymetricGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); - - virtual double valueAt(double x, double y) const; - - protected: - double m_vectorAngle; - }; - - ConicalSymetricGradientStrategy::ConicalSymetricGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) - : super(gradientVectorStart, gradientVectorEnd) - { - double dx = gradientVectorEnd.x() - gradientVectorStart.x(); - double dy = gradientVectorEnd.y() - gradientVectorStart.y(); - - // Get angle from 0 to 2 PI. - m_vectorAngle = atan2(dy, dx) + M_PI; - } - - double ConicalSymetricGradientStrategy::valueAt(double x, double y) const - { - double px = x - m_gradientVectorStart.x(); - double py = y - m_gradientVectorStart.y(); - - double angle = atan2(py, px) + M_PI; - - angle -= m_vectorAngle; - - if (angle < 0) { - angle += 2 * M_PI; - } - - double t; - - if (angle < M_PI) { - t = angle / M_PI; - } - else { - t = 1 - ((angle - M_PI) / M_PI); - } - - return t; - } - - - class GradientRepeatStrategy { - public: - GradientRepeatStrategy() {} - virtual ~GradientRepeatStrategy() {} - - virtual double valueAt(double t) const = 0; - }; - - - class GradientRepeatNoneStrategy : public GradientRepeatStrategy { - public: - static GradientRepeatNoneStrategy *instance(); - - virtual double valueAt(double t) const; - - private: - GradientRepeatNoneStrategy() {} - - static GradientRepeatNoneStrategy *m_instance; - }; - - GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::m_instance = 0; - - GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::instance() - { - if (m_instance == 0) { - m_instance = new GradientRepeatNoneStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; - } - - // Output is clamped to 0 to 1. - double GradientRepeatNoneStrategy::valueAt(double t) const - { - double value = t; - - if (t < DBL_EPSILON) { - value = 0; - } - else - if (t > 1 - DBL_EPSILON) { - value = 1; - } - - return value; - } - - - class GradientRepeatForwardsStrategy : public GradientRepeatStrategy { - public: - static GradientRepeatForwardsStrategy *instance(); - - virtual double valueAt(double t) const; - - private: - GradientRepeatForwardsStrategy() {} - - static GradientRepeatForwardsStrategy *m_instance; - }; - - GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::m_instance = 0; - - GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::instance() - { - if (m_instance == 0) { - m_instance = new GradientRepeatForwardsStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; - } - - // Output is 0 to 1, 0 to 1, 0 to 1... - double GradientRepeatForwardsStrategy::valueAt(double t) const - { - int i = static_cast(t); - - if (t < DBL_EPSILON) { - i--; - } - - double value = t - i; - - return value; - } - - - class GradientRepeatAlternateStrategy : public GradientRepeatStrategy { - public: - static GradientRepeatAlternateStrategy *instance(); - - virtual double valueAt(double t) const; - - private: - GradientRepeatAlternateStrategy() {} - - static GradientRepeatAlternateStrategy *m_instance; - }; - - GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::m_instance = 0; - - GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::instance() - { - if (m_instance == 0) { - m_instance = new GradientRepeatAlternateStrategy(); - TQ_CHECK_PTR(m_instance); - } - - return m_instance; - } - - // Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0... - double GradientRepeatAlternateStrategy::valueAt(double t) const - { - if (t < 0) { - t = -t; - } - - int i = static_cast(t); - - double value = t - i; - - if (i % 2 == 1) { - value = 1 - value; - } - - return value; - } -} - -KisGradientPainter::KisGradientPainter() - : super() -{ - m_gradient = 0; -} - -KisGradientPainter::KisGradientPainter(KisPaintDeviceSP device) : super(device), m_gradient(0) -{ -} - -bool KisGradientPainter::paintGradient(const KisPoint& gradientVectorStart, - const KisPoint& gradientVectorEnd, - enumGradientShape shape, - enumGradientRepeat repeat, - double antiAliasThreshold, - bool reverseGradient, - TQ_INT32 startx, - TQ_INT32 starty, - TQ_INT32 width, - TQ_INT32 height) -{ - m_cancelRequested = false; - - if (!m_gradient) return false; - - GradientShapeStrategy *shapeStrategy = 0; - - switch (shape) { - case GradientShapeLinear: - shapeStrategy = new LinearGradientStrategy(gradientVectorStart, gradientVectorEnd); - break; - case GradientShapeBiLinear: - shapeStrategy = new BiLinearGradientStrategy(gradientVectorStart, gradientVectorEnd); - break; - case GradientShapeRadial: - shapeStrategy = new RadialGradientStrategy(gradientVectorStart, gradientVectorEnd); - break; - case GradientShapeSquare: - shapeStrategy = new SquareGradientStrategy(gradientVectorStart, gradientVectorEnd); - break; - case GradientShapeConical: - shapeStrategy = new ConicalGradientStrategy(gradientVectorStart, gradientVectorEnd); - break; - case GradientShapeConicalSymetric: - shapeStrategy = new ConicalSymetricGradientStrategy(gradientVectorStart, gradientVectorEnd); - break; - } - TQ_CHECK_PTR(shapeStrategy); - - GradientRepeatStrategy *repeatStrategy = 0; - - switch (repeat) { - case GradientRepeatNone: - repeatStrategy = GradientRepeatNoneStrategy::instance(); - break; - case GradientRepeatForwards: - repeatStrategy = GradientRepeatForwardsStrategy::instance(); - break; - case GradientRepeatAlternate: - repeatStrategy = GradientRepeatAlternateStrategy::instance(); - break; - } - Q_ASSERT(repeatStrategy != 0); - - - //If the device has a selection only iterate over that selection - TQRect r; - if( m_device->hasSelection() ) { - r = m_device->selection()->selectedExactRect(); - startx = r.x(); - starty = r.y(); - width = r.width(); - height = r.height(); - } - - TQ_INT32 endx = startx + width - 1; - TQ_INT32 endy = starty + height - 1; - - TQImage layer (width, height, 32); - layer.setAlphaBuffer(true); - - int pixelsProcessed = 0; - int lastProgressPercent = 0; - - emit notifyProgressStage(i18n("Rendering gradient..."), 0); - - int totalPixels = width * height; - - if (antiAliasThreshold < 1 - DBL_EPSILON) { - totalPixels *= 2; - } - - for (int y = starty; y <= endy; y++) { - for (int x = startx; x <= endx; x++) { - - double t = shapeStrategy->valueAt( x, y); - t = repeatStrategy->valueAt(t); - - if (reverseGradient) { - t = 1 - t; - } - - TQColor color; - TQ_UINT8 opacity; - - m_gradient->colorAt(t, &color, &opacity); - - layer.setPixel(x - startx, y - starty, - tqRgba(color.red(), color.green(), color.blue(), opacity)); - - pixelsProcessed++; - - int progressPercent = (pixelsProcessed * 100) / totalPixels; - - if (progressPercent > lastProgressPercent) { - emit notifyProgress(progressPercent); - lastProgressPercent = progressPercent; - - if (m_cancelRequested) { - break; - } - } - if (m_cancelRequested) { - break; - } - } - } - - if (!m_cancelRequested && antiAliasThreshold < 1 - DBL_EPSILON) { - - TQColor color; - emit notifyProgressStage(i18n("Anti-aliasing gradient..."), lastProgressPercent); - TQ_UINT8 * layerPointer = layer.bits(); - for (int y = starty; y <= endy; y++) { - for (int x = startx; x <= endx; x++) { - - double maxDistance = 0; - - TQ_UINT8 redThis = layerPointer[2]; - TQ_UINT8 greenThis = layerPointer[1]; - TQ_UINT8 blueThis = layerPointer[0]; - TQ_UINT8 thisPixelOpacity = layerPointer[3]; - - for (int yOffset = -1; yOffset < 2; yOffset++) { - for (int xOffset = -1; xOffset < 2; xOffset++) { - - if (xOffset != 0 || yOffset != 0) { - int sampleX = x + xOffset; - int sampleY = y + yOffset; - - if (sampleX >= startx && sampleX <= endx && sampleY >= starty && sampleY <= endy) { - uint x = sampleX - startx; - uint y = sampleY - starty; - TQ_UINT8 * pixelPos = layer.bits() + (y * width * 4) + (x * 4); - TQ_UINT8 red = *(pixelPos +2); - TQ_UINT8 green = *(pixelPos + 1); - TQ_UINT8 blue = *pixelPos; - TQ_UINT8 opacity = *(pixelPos + 3); - - double dRed = (red * opacity - redThis * thisPixelOpacity) / 65535.0; - double dGreen = (green * opacity - greenThis * thisPixelOpacity) / 65535.0; - double dBlue = (blue * opacity - blueThis * thisPixelOpacity) / 65535.0; - - #define SQRT_3 1.7320508 - - double distance =/* sqrt(*/dRed * dRed + dGreen * dGreen + dBlue * dBlue/*) / SQRT_3*/; - - if (distance > maxDistance) { - maxDistance = distance; - } - } - } - } - } - - if (maxDistance > 3.*antiAliasThreshold*antiAliasThreshold) { - const int numSamples = 4; - - int totalRed = 0; - int totalGreen = 0; - int totalBlue = 0; - int totalOpacity = 0; - - for (int ySample = 0; ySample < numSamples; ySample++) { - for (int xSample = 0; xSample < numSamples; xSample++) { - - double sampleWidth = 1.0 / numSamples; - - double sampleX = x - 0.5 + (sampleWidth / 2) + xSample * sampleWidth; - double sampleY = y - 0.5 + (sampleWidth / 2) + ySample * sampleWidth; - - double t = shapeStrategy->valueAt(sampleX, sampleY); - t = repeatStrategy->valueAt(t); - - if (reverseGradient) { - t = 1 - t; - } - - TQ_UINT8 opacity; - - m_gradient->colorAt(t, &color, &opacity); - - totalRed += color.red(); - totalGreen += color.green(); - totalBlue += color.blue(); - totalOpacity += opacity; - } - } - - int red = totalRed / (numSamples * numSamples); - int green = totalGreen / (numSamples * numSamples); - int blue = totalBlue / (numSamples * numSamples); - int opacity = totalOpacity / (numSamples * numSamples); - - layer.setPixel(x - startx, y - starty, tqRgba(red, green, blue, opacity)); - } - - pixelsProcessed++; - - int progressPercent = (pixelsProcessed * 100) / totalPixels; - - if (progressPercent > lastProgressPercent) { - emit notifyProgress(progressPercent); - lastProgressPercent = progressPercent; - - if (m_cancelRequested) { - break; - } - } - layerPointer += 4; - } - - if (m_cancelRequested) { - break; - } - } - } - - if (!m_cancelRequested) { - kdDebug() << "Have we got a selection? " << m_device->hasSelection() << endl; - KisPaintDeviceSP dev = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temporary device for gradient"); - dev->writeBytes(layer.bits(), startx, starty, width, height); - bltSelection(startx, starty, m_compositeOp, dev, m_opacity, startx, starty, width, height); - } - delete shapeStrategy; - - emit notifyProgressDone(); - - return !m_cancelRequested; -} diff --git a/chalk/core/kis_gradient_painter.cpp b/chalk/core/kis_gradient_painter.cpp new file mode 100644 index 00000000..7c2b00b1 --- /dev/null +++ b/chalk/core/kis_gradient_painter.cpp @@ -0,0 +1,723 @@ +/* + * Copyright (c) 2004 Adrian Page + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "tqbrush.h" +#include "tqcolor.h" +#include "tqfontinfo.h" +#include "tqfontmetrics.h" +#include "tqpen.h" +#include "tqregion.h" +#include "tqwmatrix.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kis_brush.h" +#include "kis_debug_areas.h" +#include "kis_gradient.h" +#include "kis_image.h" +#include "kis_iterators_pixel.h" +#include "kis_layer.h" +#include "kis_paint_device.h" +#include "kis_pattern.h" +#include "kis_rect.h" +#include "kis_colorspace.h" +#include "kis_types.h" +#include "kis_vec.h" +#include "kis_selection.h" +#include "kis_gradient_painter.h" +#include "kis_meta_registry.h" +#include "kis_colorspace_factory_registry.h" + +namespace { + + class GradientShapeStrategy { + public: + GradientShapeStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); + virtual ~GradientShapeStrategy() {} + + virtual double valueAt(double x, double y) const = 0; + + protected: + KisPoint m_gradientVectorStart; + KisPoint m_gradientVectorEnd; + }; + + GradientShapeStrategy::GradientShapeStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) + : m_gradientVectorStart(gradientVectorStart), m_gradientVectorEnd(gradientVectorEnd) + { + } + + + class LinearGradientStrategy : public GradientShapeStrategy { + typedef GradientShapeStrategy super; + public: + LinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); + + virtual double valueAt(double x, double y) const; + + protected: + double m_normalisedVectorX; + double m_normalisedVectorY; + double m_vectorLength; + }; + + LinearGradientStrategy::LinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) + : super(gradientVectorStart, gradientVectorEnd) + { + double dx = gradientVectorEnd.x() - gradientVectorStart.x(); + double dy = gradientVectorEnd.y() - gradientVectorStart.y(); + + m_vectorLength = sqrt((dx * dx) + (dy * dy)); + + if (m_vectorLength < DBL_EPSILON) { + m_normalisedVectorX = 0; + m_normalisedVectorY = 0; + } + else { + m_normalisedVectorX = dx / m_vectorLength; + m_normalisedVectorY = dy / m_vectorLength; + } + } + + double LinearGradientStrategy::valueAt(double x, double y) const + { + double vx = x - m_gradientVectorStart.x(); + double vy = y - m_gradientVectorStart.y(); + + // Project the vector onto the normalised gradient vector. + double t = vx * m_normalisedVectorX + vy * m_normalisedVectorY; + + if (m_vectorLength < DBL_EPSILON) { + t = 0; + } + else { + // Scale to 0 to 1 over the gradient vector length. + t /= m_vectorLength; + } + + return t; + } + + + class BiLinearGradientStrategy : public LinearGradientStrategy { + typedef LinearGradientStrategy super; + public: + BiLinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); + + virtual double valueAt(double x, double y) const; + }; + + BiLinearGradientStrategy::BiLinearGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) + : super(gradientVectorStart, gradientVectorEnd) + { + } + + double BiLinearGradientStrategy::valueAt(double x, double y) const + { + double t = super::valueAt(x, y); + + // Reflect + if (t < -DBL_EPSILON) { + t = -t; + } + + return t; + } + + + class RadialGradientStrategy : public GradientShapeStrategy { + typedef GradientShapeStrategy super; + public: + RadialGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); + + virtual double valueAt(double x, double y) const; + + protected: + double m_radius; + }; + + RadialGradientStrategy::RadialGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) + : super(gradientVectorStart, gradientVectorEnd) + { + double dx = gradientVectorEnd.x() - gradientVectorStart.x(); + double dy = gradientVectorEnd.y() - gradientVectorStart.y(); + + m_radius = sqrt((dx * dx) + (dy * dy)); + } + + double RadialGradientStrategy::valueAt(double x, double y) const + { + double dx = x - m_gradientVectorStart.x(); + double dy = y - m_gradientVectorStart.y(); + + double distance = sqrt((dx * dx) + (dy * dy)); + + double t; + + if (m_radius < DBL_EPSILON) { + t = 0; + } + else { + t = distance / m_radius; + } + + return t; + } + + + class SquareGradientStrategy : public GradientShapeStrategy { + typedef GradientShapeStrategy super; + public: + SquareGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); + + virtual double valueAt(double x, double y) const; + + protected: + double m_normalisedVectorX; + double m_normalisedVectorY; + double m_vectorLength; + }; + + SquareGradientStrategy::SquareGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) + : super(gradientVectorStart, gradientVectorEnd) + { + double dx = gradientVectorEnd.x() - gradientVectorStart.x(); + double dy = gradientVectorEnd.y() - gradientVectorStart.y(); + + m_vectorLength = sqrt((dx * dx) + (dy * dy)); + + if (m_vectorLength < DBL_EPSILON) { + m_normalisedVectorX = 0; + m_normalisedVectorY = 0; + } + else { + m_normalisedVectorX = dx / m_vectorLength; + m_normalisedVectorY = dy / m_vectorLength; + } + } + + double SquareGradientStrategy::valueAt(double x, double y) const + { + double px = x - m_gradientVectorStart.x(); + double py = y - m_gradientVectorStart.y(); + + double distance1 = 0; + double distance2 = 0; + + if (m_vectorLength > DBL_EPSILON) { + + // Point to line distance is: + // distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / m_vectorLength; + // + // Here l0 = (0, 0) and |l1 - l0| = 1 + + distance1 = -m_normalisedVectorY * px + m_normalisedVectorX * py; + distance1 = fabs(distance1); + + // Rotate point by 90 degrees and get the distance to the perpendicular + distance2 = -m_normalisedVectorY * -py + m_normalisedVectorX * px; + distance2 = fabs(distance2); + } + + double t = TQMAX(distance1, distance2) / m_vectorLength; + + return t; + } + + + class ConicalGradientStrategy : public GradientShapeStrategy { + typedef GradientShapeStrategy super; + public: + ConicalGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); + + virtual double valueAt(double x, double y) const; + + protected: + double m_vectorAngle; + }; + + ConicalGradientStrategy::ConicalGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) + : super(gradientVectorStart, gradientVectorEnd) + { + double dx = gradientVectorEnd.x() - gradientVectorStart.x(); + double dy = gradientVectorEnd.y() - gradientVectorStart.y(); + + // Get angle from 0 to 2 PI. + m_vectorAngle = atan2(dy, dx) + M_PI; + } + + double ConicalGradientStrategy::valueAt(double x, double y) const + { + double px = x - m_gradientVectorStart.x(); + double py = y - m_gradientVectorStart.y(); + + double angle = atan2(py, px) + M_PI; + + angle -= m_vectorAngle; + + if (angle < 0) { + angle += 2 * M_PI; + } + + double t = angle / (2 * M_PI); + + return t; + } + + + class ConicalSymetricGradientStrategy : public GradientShapeStrategy { + typedef GradientShapeStrategy super; + public: + ConicalSymetricGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd); + + virtual double valueAt(double x, double y) const; + + protected: + double m_vectorAngle; + }; + + ConicalSymetricGradientStrategy::ConicalSymetricGradientStrategy(const KisPoint& gradientVectorStart, const KisPoint& gradientVectorEnd) + : super(gradientVectorStart, gradientVectorEnd) + { + double dx = gradientVectorEnd.x() - gradientVectorStart.x(); + double dy = gradientVectorEnd.y() - gradientVectorStart.y(); + + // Get angle from 0 to 2 PI. + m_vectorAngle = atan2(dy, dx) + M_PI; + } + + double ConicalSymetricGradientStrategy::valueAt(double x, double y) const + { + double px = x - m_gradientVectorStart.x(); + double py = y - m_gradientVectorStart.y(); + + double angle = atan2(py, px) + M_PI; + + angle -= m_vectorAngle; + + if (angle < 0) { + angle += 2 * M_PI; + } + + double t; + + if (angle < M_PI) { + t = angle / M_PI; + } + else { + t = 1 - ((angle - M_PI) / M_PI); + } + + return t; + } + + + class GradientRepeatStrategy { + public: + GradientRepeatStrategy() {} + virtual ~GradientRepeatStrategy() {} + + virtual double valueAt(double t) const = 0; + }; + + + class GradientRepeatNoneStrategy : public GradientRepeatStrategy { + public: + static GradientRepeatNoneStrategy *instance(); + + virtual double valueAt(double t) const; + + private: + GradientRepeatNoneStrategy() {} + + static GradientRepeatNoneStrategy *m_instance; + }; + + GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::m_instance = 0; + + GradientRepeatNoneStrategy *GradientRepeatNoneStrategy::instance() + { + if (m_instance == 0) { + m_instance = new GradientRepeatNoneStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; + } + + // Output is clamped to 0 to 1. + double GradientRepeatNoneStrategy::valueAt(double t) const + { + double value = t; + + if (t < DBL_EPSILON) { + value = 0; + } + else + if (t > 1 - DBL_EPSILON) { + value = 1; + } + + return value; + } + + + class GradientRepeatForwardsStrategy : public GradientRepeatStrategy { + public: + static GradientRepeatForwardsStrategy *instance(); + + virtual double valueAt(double t) const; + + private: + GradientRepeatForwardsStrategy() {} + + static GradientRepeatForwardsStrategy *m_instance; + }; + + GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::m_instance = 0; + + GradientRepeatForwardsStrategy *GradientRepeatForwardsStrategy::instance() + { + if (m_instance == 0) { + m_instance = new GradientRepeatForwardsStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; + } + + // Output is 0 to 1, 0 to 1, 0 to 1... + double GradientRepeatForwardsStrategy::valueAt(double t) const + { + int i = static_cast(t); + + if (t < DBL_EPSILON) { + i--; + } + + double value = t - i; + + return value; + } + + + class GradientRepeatAlternateStrategy : public GradientRepeatStrategy { + public: + static GradientRepeatAlternateStrategy *instance(); + + virtual double valueAt(double t) const; + + private: + GradientRepeatAlternateStrategy() {} + + static GradientRepeatAlternateStrategy *m_instance; + }; + + GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::m_instance = 0; + + GradientRepeatAlternateStrategy *GradientRepeatAlternateStrategy::instance() + { + if (m_instance == 0) { + m_instance = new GradientRepeatAlternateStrategy(); + TQ_CHECK_PTR(m_instance); + } + + return m_instance; + } + + // Output is 0 to 1, 1 to 0, 0 to 1, 1 to 0... + double GradientRepeatAlternateStrategy::valueAt(double t) const + { + if (t < 0) { + t = -t; + } + + int i = static_cast(t); + + double value = t - i; + + if (i % 2 == 1) { + value = 1 - value; + } + + return value; + } +} + +KisGradientPainter::KisGradientPainter() + : super() +{ + m_gradient = 0; +} + +KisGradientPainter::KisGradientPainter(KisPaintDeviceSP device) : super(device), m_gradient(0) +{ +} + +bool KisGradientPainter::paintGradient(const KisPoint& gradientVectorStart, + const KisPoint& gradientVectorEnd, + enumGradientShape shape, + enumGradientRepeat repeat, + double antiAliasThreshold, + bool reverseGradient, + TQ_INT32 startx, + TQ_INT32 starty, + TQ_INT32 width, + TQ_INT32 height) +{ + m_cancelRequested = false; + + if (!m_gradient) return false; + + GradientShapeStrategy *shapeStrategy = 0; + + switch (shape) { + case GradientShapeLinear: + shapeStrategy = new LinearGradientStrategy(gradientVectorStart, gradientVectorEnd); + break; + case GradientShapeBiLinear: + shapeStrategy = new BiLinearGradientStrategy(gradientVectorStart, gradientVectorEnd); + break; + case GradientShapeRadial: + shapeStrategy = new RadialGradientStrategy(gradientVectorStart, gradientVectorEnd); + break; + case GradientShapeSquare: + shapeStrategy = new SquareGradientStrategy(gradientVectorStart, gradientVectorEnd); + break; + case GradientShapeConical: + shapeStrategy = new ConicalGradientStrategy(gradientVectorStart, gradientVectorEnd); + break; + case GradientShapeConicalSymetric: + shapeStrategy = new ConicalSymetricGradientStrategy(gradientVectorStart, gradientVectorEnd); + break; + } + TQ_CHECK_PTR(shapeStrategy); + + GradientRepeatStrategy *repeatStrategy = 0; + + switch (repeat) { + case GradientRepeatNone: + repeatStrategy = GradientRepeatNoneStrategy::instance(); + break; + case GradientRepeatForwards: + repeatStrategy = GradientRepeatForwardsStrategy::instance(); + break; + case GradientRepeatAlternate: + repeatStrategy = GradientRepeatAlternateStrategy::instance(); + break; + } + Q_ASSERT(repeatStrategy != 0); + + + //If the device has a selection only iterate over that selection + TQRect r; + if( m_device->hasSelection() ) { + r = m_device->selection()->selectedExactRect(); + startx = r.x(); + starty = r.y(); + width = r.width(); + height = r.height(); + } + + TQ_INT32 endx = startx + width - 1; + TQ_INT32 endy = starty + height - 1; + + TQImage layer (width, height, 32); + layer.setAlphaBuffer(true); + + int pixelsProcessed = 0; + int lastProgressPercent = 0; + + emit notifyProgressStage(i18n("Rendering gradient..."), 0); + + int totalPixels = width * height; + + if (antiAliasThreshold < 1 - DBL_EPSILON) { + totalPixels *= 2; + } + + for (int y = starty; y <= endy; y++) { + for (int x = startx; x <= endx; x++) { + + double t = shapeStrategy->valueAt( x, y); + t = repeatStrategy->valueAt(t); + + if (reverseGradient) { + t = 1 - t; + } + + TQColor color; + TQ_UINT8 opacity; + + m_gradient->colorAt(t, &color, &opacity); + + layer.setPixel(x - startx, y - starty, + tqRgba(color.red(), color.green(), color.blue(), opacity)); + + pixelsProcessed++; + + int progressPercent = (pixelsProcessed * 100) / totalPixels; + + if (progressPercent > lastProgressPercent) { + emit notifyProgress(progressPercent); + lastProgressPercent = progressPercent; + + if (m_cancelRequested) { + break; + } + } + if (m_cancelRequested) { + break; + } + } + } + + if (!m_cancelRequested && antiAliasThreshold < 1 - DBL_EPSILON) { + + TQColor color; + emit notifyProgressStage(i18n("Anti-aliasing gradient..."), lastProgressPercent); + TQ_UINT8 * layerPointer = layer.bits(); + for (int y = starty; y <= endy; y++) { + for (int x = startx; x <= endx; x++) { + + double maxDistance = 0; + + TQ_UINT8 redThis = layerPointer[2]; + TQ_UINT8 greenThis = layerPointer[1]; + TQ_UINT8 blueThis = layerPointer[0]; + TQ_UINT8 thisPixelOpacity = layerPointer[3]; + + for (int yOffset = -1; yOffset < 2; yOffset++) { + for (int xOffset = -1; xOffset < 2; xOffset++) { + + if (xOffset != 0 || yOffset != 0) { + int sampleX = x + xOffset; + int sampleY = y + yOffset; + + if (sampleX >= startx && sampleX <= endx && sampleY >= starty && sampleY <= endy) { + uint x = sampleX - startx; + uint y = sampleY - starty; + TQ_UINT8 * pixelPos = layer.bits() + (y * width * 4) + (x * 4); + TQ_UINT8 red = *(pixelPos +2); + TQ_UINT8 green = *(pixelPos + 1); + TQ_UINT8 blue = *pixelPos; + TQ_UINT8 opacity = *(pixelPos + 3); + + double dRed = (red * opacity - redThis * thisPixelOpacity) / 65535.0; + double dGreen = (green * opacity - greenThis * thisPixelOpacity) / 65535.0; + double dBlue = (blue * opacity - blueThis * thisPixelOpacity) / 65535.0; + + #define SQRT_3 1.7320508 + + double distance =/* sqrt(*/dRed * dRed + dGreen * dGreen + dBlue * dBlue/*) / SQRT_3*/; + + if (distance > maxDistance) { + maxDistance = distance; + } + } + } + } + } + + if (maxDistance > 3.*antiAliasThreshold*antiAliasThreshold) { + const int numSamples = 4; + + int totalRed = 0; + int totalGreen = 0; + int totalBlue = 0; + int totalOpacity = 0; + + for (int ySample = 0; ySample < numSamples; ySample++) { + for (int xSample = 0; xSample < numSamples; xSample++) { + + double sampleWidth = 1.0 / numSamples; + + double sampleX = x - 0.5 + (sampleWidth / 2) + xSample * sampleWidth; + double sampleY = y - 0.5 + (sampleWidth / 2) + ySample * sampleWidth; + + double t = shapeStrategy->valueAt(sampleX, sampleY); + t = repeatStrategy->valueAt(t); + + if (reverseGradient) { + t = 1 - t; + } + + TQ_UINT8 opacity; + + m_gradient->colorAt(t, &color, &opacity); + + totalRed += color.red(); + totalGreen += color.green(); + totalBlue += color.blue(); + totalOpacity += opacity; + } + } + + int red = totalRed / (numSamples * numSamples); + int green = totalGreen / (numSamples * numSamples); + int blue = totalBlue / (numSamples * numSamples); + int opacity = totalOpacity / (numSamples * numSamples); + + layer.setPixel(x - startx, y - starty, tqRgba(red, green, blue, opacity)); + } + + pixelsProcessed++; + + int progressPercent = (pixelsProcessed * 100) / totalPixels; + + if (progressPercent > lastProgressPercent) { + emit notifyProgress(progressPercent); + lastProgressPercent = progressPercent; + + if (m_cancelRequested) { + break; + } + } + layerPointer += 4; + } + + if (m_cancelRequested) { + break; + } + } + } + + if (!m_cancelRequested) { + kdDebug() << "Have we got a selection? " << m_device->hasSelection() << endl; + KisPaintDeviceSP dev = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()->getRGB8(), "temporary device for gradient"); + dev->writeBytes(layer.bits(), startx, starty, width, height); + bltSelection(startx, starty, m_compositeOp, dev, m_opacity, startx, starty, width, height); + } + delete shapeStrategy; + + emit notifyProgressDone(); + + return !m_cancelRequested; +} diff --git a/chalk/core/kis_group_layer.cc b/chalk/core/kis_group_layer.cc deleted file mode 100644 index 290d19f5..00000000 --- a/chalk/core/kis_group_layer.cc +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (c) 2005 Casper Boemann - * - * this program is free software; you can redistribute it and/or modify - * it under the terms of the gnu general public license as published by - * the free software foundation; either version 2 of the license, or - * (at your option) any later version. - * - * this program is distributed in the hope that it will be useful, - * but without any warranty; without even the implied warranty of - * merchantability or fitness for a particular purpose. see the - * gnu general public license for more details. - * - * you should have received a copy of the gnu general public license - * along with this program; if not, write to the free software - * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. - */ - -#include -#include -#include -#include - -#include "kis_types.h" -#include "kis_layer.h" -#include "kis_group_layer.h" -#include "kis_layer_visitor.h" -#include "kis_debug_areas.h" -#include "kis_image.h" -#include "kis_paint_device.h" -#include "kis_merge_visitor.h" -#include "kis_fill_painter.h" - -KisGroupLayer::KisGroupLayer(KisImage *img, const TQString &name, TQ_UINT8 opacity) : - super(img, name, opacity), - m_x(0), - m_y(0) -{ - m_projection = new KisPaintDevice(this, img->colorSpace(), name.latin1()); -} - -KisGroupLayer::KisGroupLayer(const KisGroupLayer &rhs) : - super(rhs), - m_x(rhs.m_x), - m_y(rhs.m_y) -{ - for(vKisLayerSP_cit it = rhs.m_layers.begin(); it != rhs.m_layers.end(); ++it) - { - this->addLayer(it->data()->clone(), 0); - } - m_projection = new KisPaintDevice(*rhs.m_projection.data()); - m_projection->setParentLayer(this); -} - -KisLayerSP KisGroupLayer::clone() const -{ - return new KisGroupLayer(*this); -} - -KisGroupLayer::~KisGroupLayer() -{ - m_layers.clear(); -} - - -void KisGroupLayer::setDirty(bool propagate) -{ - KisLayer::setDirty(propagate); - if (propagate) emit (sigDirty(m_dirtyRect)); -} - -void KisGroupLayer::setDirty(const TQRect & rc, bool propagate) -{ - KisLayer::setDirty(rc, propagate); - if (propagate) emit sigDirty(rc); -} - -void KisGroupLayer::resetProjection(KisPaintDevice* to) -{ - if (to) - m_projection = new KisPaintDevice(*to); /// XXX ### look into Copy on Write here (CoW) - else - m_projection = new KisPaintDevice(this, image()->colorSpace(), name().latin1()); -} - -bool KisGroupLayer::paintLayerInducesProjectionOptimization(KisPaintLayer* l) { - return l && l->paintDevice()->colorSpace() == m_image->colorSpace() && l->visible() - && l->opacity() == OPACITY_OPAQUE && !l->temporaryTarget() && !l->hasMask(); -} - -KisPaintDeviceSP KisGroupLayer::projection(const TQRect & rect) -{ - // We don't have a parent, and we've got only one child: abuse the child's - // paint device as the projection if the child is visible and 100% opaque - if (parent() == 0 && childCount() == 1) { - KisPaintLayerSP l = dynamic_cast(firstChild().data()); - if (paintLayerInducesProjectionOptimization(l)) { - l->setClean(rect); - setClean(rect); - return l->paintDevice(); - } - } - // No need for updates, we're clean - if (!dirty()) { - return m_projection; - } - // No need for updates -- the desired area wasn't dirty - if (!rect.intersects(m_dirtyRect)) { - return m_projection; - } - - - // Okay, we need to update the intersection between - // what's dirty and what's asked us to be updated. - // XXX Nooo, that doesn't work, since the call to setClean following this, is actually: - // m_dirtyRect = TQRect(); So the non-intersecting part gets brilliantly lost otherwise. - const TQRect rc = m_dirtyRect;//rect.intersect(m_dirtyRect); - - updateProjection(rc); - setClean(rect); - - return m_projection; -} - -uint KisGroupLayer::childCount() const -{ - return m_layers.count(); -} - -KisLayerSP KisGroupLayer::firstChild() const -{ - return at(0); -} - -KisLayerSP KisGroupLayer::lastChild() const -{ - return at(childCount() - 1); -} - -KisLayerSP KisGroupLayer::at(int index) const -{ - if (childCount() && index >= 0 && kClamp(uint(index), uint(0), childCount() - 1) == uint(index)) - return m_layers.at(reverseIndex(index)); - return 0; -} - -int KisGroupLayer::index(KisLayerSP layer) const -{ - if (layer->parent().data() == this) - return layer->index(); - return -1; -} - -void KisGroupLayer::setIndex(KisLayerSP layer, int index) -{ - if (layer->parent().data() != this) - return; - //TODO optimize - removeLayer(layer); - addLayer(layer, index); -} - -bool KisGroupLayer::addLayer(KisLayerSP newLayer, int x) -{ - if (x < 0 || kClamp(uint(x), uint(0), childCount()) != uint(x) || - newLayer->parent() || m_layers.contains(newLayer)) - { - kdWarning() << "invalid input to KisGroupLayer::addLayer(KisLayerSP newLayer, int x)!" << endl; - return false; - } - uint index(x); - if (index == 0) - m_layers.append(newLayer); - else - m_layers.insert(m_layers.begin() + reverseIndex(index) + 1, newLayer); - for (uint i = childCount() - 1; i > index; i--) - at(i)->m_index++; - newLayer->m_parent = this; - newLayer->m_index = index; - newLayer->setImage(image()); - newLayer->setDirty(newLayer->extent()); - setDirty(); - return true; -} - -bool KisGroupLayer::addLayer(KisLayerSP newLayer, KisLayerSP aboveThis) -{ - if (aboveThis && aboveThis->parent().data() != this) - { - kdWarning() << "invalid input to KisGroupLayer::addLayer(KisLayerSP newLayer, KisLayerSP aboveThis)!" << endl; - return false; - } - return addLayer(newLayer, aboveThis ? aboveThis->index() : childCount()); -} - -bool KisGroupLayer::removeLayer(int x) -{ - if (x >= 0 && kClamp(uint(x), uint(0), childCount() - 1) == uint(x)) - { - uint index(x); - for (uint i = childCount() - 1; i > index; i--) - at(i)->m_index--; - KisLayerSP removedLayer = at(index); - - removedLayer->m_parent = 0; - removedLayer->m_index = -1; - m_layers.erase(m_layers.begin() + reverseIndex(index)); - setDirty(removedLayer->extent()); - if (childCount() < 1) { - // No children, nothing to show for it. - m_projection->clear(); - setDirty(); - } - return true; - } - kdWarning() << "invalid input to KisGroupLayer::removeLayer()!" << endl; - return false; -} - -bool KisGroupLayer::removeLayer(KisLayerSP layer) -{ - if (layer->parent().data() != this) - { - kdWarning() << "invalid input to KisGroupLayer::removeLayer()!" << endl; - return false; - } - - return removeLayer(layer->index()); -} - -void KisGroupLayer::setImage(KisImage *image) -{ - super::setImage(image); - for (vKisLayerSP_it it = m_layers.begin(); it != m_layers.end(); ++it) - { - (*it)->setImage(image); - } -} - -TQRect KisGroupLayer::extent() const -{ - TQRect groupExtent; - - for (vKisLayerSP_cit it = m_layers.begin(); it != m_layers.end(); ++it) - { - groupExtent |= (*it)->extent(); - } - - return groupExtent; -} - -TQRect KisGroupLayer::exactBounds() const -{ - TQRect groupExactBounds; - - for (vKisLayerSP_cit it = m_layers.begin(); it != m_layers.end(); ++it) - { - groupExactBounds |= (*it)->exactBounds(); - } - - return groupExactBounds; -} - -TQ_INT32 KisGroupLayer::x() const -{ - return m_x; -} - -void KisGroupLayer::setX(TQ_INT32 x) -{ - TQ_INT32 delta = x - m_x; - - for (vKisLayerSP_cit it = m_layers.begin(); it != m_layers.end(); ++it) - { - KisLayerSP layer = *it; - layer->setX(layer->x() + delta); - } - m_x = x; -} - -TQ_INT32 KisGroupLayer::y() const -{ - return m_y; -} - -void KisGroupLayer::setY(TQ_INT32 y) -{ - TQ_INT32 delta = y - m_y; - - for (vKisLayerSP_cit it = m_layers.begin(); it != m_layers.end(); ++it) - { - KisLayerSP layer = *it; - layer->setY(layer->y() + delta); - } - - m_y = y; -} - -TQImage KisGroupLayer::createThumbnail(TQ_INT32 w, TQ_INT32 h) -{ - return m_projection->createThumbnail(w, h); -} - -void KisGroupLayer::updateProjection(const TQRect & rc) -{ - if (!m_dirtyRect.isValid()) return; - - // Get the first layer in this group to start compositing with - KisLayerSP child = lastChild(); - - // No child -- clear the projection. Without children, a group layer is empty. - if (!child) m_projection->clear(); - - KisLayerSP startWith = 0; - KisAdjustmentLayerSP adjLayer = 0; - KisLayerSP tmpPaintLayer = 0; - - // If this is the rootlayer, don't do anything with adj. layers that are below the - // first paintlayer - bool gotPaintLayer = (parent() != 0); - - // Look through all the child layers, searching for the first dirty layer - // if it's found, and if we have found an adj. layer before the the dirty layer, - // composite from the first adjustment layer searching back from the first dirty layer - while (child) { - KisAdjustmentLayerSP tmpAdjLayer = dynamic_cast(child.data()); - if (tmpAdjLayer) { - if (gotPaintLayer) { - // If this adjustment layer is dirty, start compositing with the - // previous layer, if there's one. - if (tmpAdjLayer->dirty(rc) && adjLayer != 0 && adjLayer->visible()) { - startWith = adjLayer->prevSibling(); - break; - } - else if (tmpAdjLayer->visible() && !tmpAdjLayer->dirty(rc)) { - // This is the first adj. layer that is not dirty -- the perfect starting point - adjLayer = tmpAdjLayer; - } - else { - startWith = tmpPaintLayer; - } - } - } - else { - tmpPaintLayer = child; - gotPaintLayer = true; - // A non-adjustmentlayer that's dirty; if there's an adjustmentlayer - // with a cache, we'll start from there. - if (child->dirty(rc)) { - if (adjLayer != 0 && adjLayer->visible()) { - // the first layer on top of the adj. layer - startWith = adjLayer->prevSibling(); - } - else { - startWith = child; - } - // break here: if there's no adj layer, we'll start with the layer->lastChild - break; - } - } - child = child->prevSibling(); - } - - if (adjLayer != 0 && startWith == 0 && gotPaintLayer && adjLayer->prevSibling()) { - startWith = adjLayer->prevSibling(); - } - - // No adj layer -- all layers inside the group must be recomposited - if (adjLayer == 0) { - startWith = lastChild(); - } - - if (startWith == 0) { - return; - } - - bool first = true; // The first layer in a stack needs special compositing - - // Fill the projection either with the cached data, or erase it. - KisFillPainter gc(m_projection); - if (adjLayer != 0) { - gc.bitBlt(rc.left(), rc.top(), - COMPOSITE_COPY, adjLayer->cachedPaintDevice(), OPACITY_OPAQUE, - rc.left(), rc.top(), rc.width(), rc.height()); - first = false; - } - else { - gc.eraseRect(rc); - first = true; - } - gc.end(); - - KisMergeVisitor visitor(m_projection, rc); - - child = startWith; - - while(child) - { - if(first) - { - // Copy the lowest layer rather than compositing it with the background - // or an empty image. This means the layer's composite op is ignored, - // which is consistent with Photoshop and gimp. - const KisCompositeOp cop = child->compositeOp(); - const bool block = child->signalsBlocked(); - child->blockSignals(true); - // Composite op copy doesn't take a mask/selection into account, so we need - // to make a difference between a paintlayer with a mask, and one without - KisPaintLayer* l = dynamic_cast(child.data()); - if (l && l->hasMask()) - child->m_compositeOp = COMPOSITE_OVER; - else - child->m_compositeOp = COMPOSITE_COPY; - child->blockSignals(block); - child->accept(visitor); - child->blockSignals(true); - child->m_compositeOp = cop; - child->blockSignals(block); - first = false; - } - else - child->accept(visitor); - - child = child->prevSibling(); - } -} - -#include "kis_group_layer.moc" diff --git a/chalk/core/kis_group_layer.cpp b/chalk/core/kis_group_layer.cpp new file mode 100644 index 00000000..290d19f5 --- /dev/null +++ b/chalk/core/kis_group_layer.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2005 Casper Boemann + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. + */ + +#include +#include +#include +#include + +#include "kis_types.h" +#include "kis_layer.h" +#include "kis_group_layer.h" +#include "kis_layer_visitor.h" +#include "kis_debug_areas.h" +#include "kis_image.h" +#include "kis_paint_device.h" +#include "kis_merge_visitor.h" +#include "kis_fill_painter.h" + +KisGroupLayer::KisGroupLayer(KisImage *img, const TQString &name, TQ_UINT8 opacity) : + super(img, name, opacity), + m_x(0), + m_y(0) +{ + m_projection = new KisPaintDevice(this, img->colorSpace(), name.latin1()); +} + +KisGroupLayer::KisGroupLayer(const KisGroupLayer &rhs) : + super(rhs), + m_x(rhs.m_x), + m_y(rhs.m_y) +{ + for(vKisLayerSP_cit it = rhs.m_layers.begin(); it != rhs.m_layers.end(); ++it) + { + this->addLayer(it->data()->clone(), 0); + } + m_projection = new KisPaintDevice(*rhs.m_projection.data()); + m_projection->setParentLayer(this); +} + +KisLayerSP KisGroupLayer::clone() const +{ + return new KisGroupLayer(*this); +} + +KisGroupLayer::~KisGroupLayer() +{ + m_layers.clear(); +} + + +void KisGroupLayer::setDirty(bool propagate) +{ + KisLayer::setDirty(propagate); + if (propagate) emit (sigDirty(m_dirtyRect)); +} + +void KisGroupLayer::setDirty(const TQRect & rc, bool propagate) +{ + KisLayer::setDirty(rc, propagate); + if (propagate) emit sigDirty(rc); +} + +void KisGroupLayer::resetProjection(KisPaintDevice* to) +{ + if (to) + m_projection = new KisPaintDevice(*to); /// XXX ### look into Copy on Write here (CoW) + else + m_projection = new KisPaintDevice(this, image()->colorSpace(), name().latin1()); +} + +bool KisGroupLayer::paintLayerInducesProjectionOptimization(KisPaintLayer* l) { + return l && l->paintDevice()->colorSpace() == m_image->colorSpace() && l->visible() + && l->opacity() == OPACITY_OPAQUE && !l->temporaryTarget() && !l->hasMask(); +} + +KisPaintDeviceSP KisGroupLayer::projection(const TQRect & rect) +{ + // We don't have a parent, and we've got only one child: abuse the child's + // paint device as the projection if the child is visible and 100% opaque + if (parent() == 0 && childCount() == 1) { + KisPaintLayerSP l = dynamic_cast(firstChild().data()); + if (paintLayerInducesProjectionOptimization(l)) { + l->setClean(rect); + setClean(rect); + return l->paintDevice(); + } + } + // No need for updates, we're clean + if (!dirty()) { + return m_projection; + } + // No need for updates -- the desired area wasn't dirty + if (!rect.intersects(m_dirtyRect)) { + return m_projection; + } + + + // Okay, we need to update the intersection between + // what's dirty and what's asked us to be updated. + // XXX Nooo, that doesn't work, since the call to setClean following this, is actually: + // m_dirtyRect = TQRect(); So the non-intersecting part gets brilliantly lost otherwise. + const TQRect rc = m_dirtyRect;//rect.intersect(m_dirtyRect); + + updateProjection(rc); + setClean(rect); + + return m_projection; +} + +uint KisGroupLayer::childCount() const +{ + return m_layers.count(); +} + +KisLayerSP KisGroupLayer::firstChild() const +{ + return at(0); +} + +KisLayerSP KisGroupLayer::lastChild() const +{ + return at(childCount() - 1); +} + +KisLayerSP KisGroupLayer::at(int index) const +{ + if (childCount() && index >= 0 && kClamp(uint(index), uint(0), childCount() - 1) == uint(index)) + return m_layers.at(reverseIndex(index)); + return 0; +} + +int KisGroupLayer::index(KisLayerSP layer) const +{ + if (layer->parent().data() == this) + return layer->index(); + return -1; +} + +void KisGroupLayer::setIndex(KisLayerSP layer, int index) +{ + if (layer->parent().data() != this) + return; + //TODO optimize + removeLayer(layer); + addLayer(layer, index); +} + +bool KisGroupLayer::addLayer(KisLayerSP newLayer, int x) +{ + if (x < 0 || kClamp(uint(x), uint(0), childCount()) != uint(x) || + newLayer->parent() || m_layers.contains(newLayer)) + { + kdWarning() << "invalid input to KisGroupLayer::addLayer(KisLayerSP newLayer, int x)!" << endl; + return false; + } + uint index(x); + if (index == 0) + m_layers.append(newLayer); + else + m_layers.insert(m_layers.begin() + reverseIndex(index) + 1, newLayer); + for (uint i = childCount() - 1; i > index; i--) + at(i)->m_index++; + newLayer->m_parent = this; + newLayer->m_index = index; + newLayer->setImage(image()); + newLayer->setDirty(newLayer->extent()); + setDirty(); + return true; +} + +bool KisGroupLayer::addLayer(KisLayerSP newLayer, KisLayerSP aboveThis) +{ + if (aboveThis && aboveThis->parent().data() != this) + { + kdWarning() << "invalid input to KisGroupLayer::addLayer(KisLayerSP newLayer, KisLayerSP aboveThis)!" << endl; + return false; + } + return addLayer(newLayer, aboveThis ? aboveThis->index() : childCount()); +} + +bool KisGroupLayer::removeLayer(int x) +{ + if (x >= 0 && kClamp(uint(x), uint(0), childCount() - 1) == uint(x)) + { + uint index(x); + for (uint i = childCount() - 1; i > index; i--) + at(i)->m_index--; + KisLayerSP removedLayer = at(index); + + removedLayer->m_parent = 0; + removedLayer->m_index = -1; + m_layers.erase(m_layers.begin() + reverseIndex(index)); + setDirty(removedLayer->extent()); + if (childCount() < 1) { + // No children, nothing to show for it. + m_projection->clear(); + setDirty(); + } + return true; + } + kdWarning() << "invalid input to KisGroupLayer::removeLayer()!" << endl; + return false; +} + +bool KisGroupLayer::removeLayer(KisLayerSP layer) +{ + if (layer->parent().data() != this) + { + kdWarning() << "invalid input to KisGroupLayer::removeLayer()!" << endl; + return false; + } + + return removeLayer(layer->index()); +} + +void KisGroupLayer::setImage(KisImage *image) +{ + super::setImage(image); + for (vKisLayerSP_it it = m_layers.begin(); it != m_layers.end(); ++it) + { + (*it)->setImage(image); + } +} + +TQRect KisGroupLayer::extent() const +{ + TQRect groupExtent; + + for (vKisLayerSP_cit it = m_layers.begin(); it != m_layers.end(); ++it) + { + groupExtent |= (*it)->extent(); + } + + return groupExtent; +} + +TQRect KisGroupLayer::exactBounds() const +{ + TQRect groupExactBounds; + + for (vKisLayerSP_cit it = m_layers.begin(); it != m_layers.end(); ++it) + { + groupExactBounds |= (*it)->exactBounds(); + } + + return groupExactBounds; +} + +TQ_INT32 KisGroupLayer::x() const +{ + return m_x; +} + +void KisGroupLayer::setX(TQ_INT32 x) +{ + TQ_INT32 delta = x - m_x; + + for (vKisLayerSP_cit it = m_layers.begin(); it != m_layers.end(); ++it) + { + KisLayerSP layer = *it; + layer->setX(layer->x() + delta); + } + m_x = x; +} + +TQ_INT32 KisGroupLayer::y() const +{ + return m_y; +} + +void KisGroupLayer::setY(TQ_INT32 y) +{ + TQ_INT32 delta = y - m_y; + + for (vKisLayerSP_cit it = m_layers.begin(); it != m_layers.end(); ++it) + { + KisLayerSP layer = *it; + layer->setY(layer->y() + delta); + } + + m_y = y; +} + +TQImage KisGroupLayer::createThumbnail(TQ_INT32 w, TQ_INT32 h) +{ + return m_projection->createThumbnail(w, h); +} + +void KisGroupLayer::updateProjection(const TQRect & rc) +{ + if (!m_dirtyRect.isValid()) return; + + // Get the first layer in this group to start compositing with + KisLayerSP child = lastChild(); + + // No child -- clear the projection. Without children, a group layer is empty. + if (!child) m_projection->clear(); + + KisLayerSP startWith = 0; + KisAdjustmentLayerSP adjLayer = 0; + KisLayerSP tmpPaintLayer = 0; + + // If this is the rootlayer, don't do anything with adj. layers that are below the + // first paintlayer + bool gotPaintLayer = (parent() != 0); + + // Look through all the child layers, searching for the first dirty layer + // if it's found, and if we have found an adj. layer before the the dirty layer, + // composite from the first adjustment layer searching back from the first dirty layer + while (child) { + KisAdjustmentLayerSP tmpAdjLayer = dynamic_cast(child.data()); + if (tmpAdjLayer) { + if (gotPaintLayer) { + // If this adjustment layer is dirty, start compositing with the + // previous layer, if there's one. + if (tmpAdjLayer->dirty(rc) && adjLayer != 0 && adjLayer->visible()) { + startWith = adjLayer->prevSibling(); + break; + } + else if (tmpAdjLayer->visible() && !tmpAdjLayer->dirty(rc)) { + // This is the first adj. layer that is not dirty -- the perfect starting point + adjLayer = tmpAdjLayer; + } + else { + startWith = tmpPaintLayer; + } + } + } + else { + tmpPaintLayer = child; + gotPaintLayer = true; + // A non-adjustmentlayer that's dirty; if there's an adjustmentlayer + // with a cache, we'll start from there. + if (child->dirty(rc)) { + if (adjLayer != 0 && adjLayer->visible()) { + // the first layer on top of the adj. layer + startWith = adjLayer->prevSibling(); + } + else { + startWith = child; + } + // break here: if there's no adj layer, we'll start with the layer->lastChild + break; + } + } + child = child->prevSibling(); + } + + if (adjLayer != 0 && startWith == 0 && gotPaintLayer && adjLayer->prevSibling()) { + startWith = adjLayer->prevSibling(); + } + + // No adj layer -- all layers inside the group must be recomposited + if (adjLayer == 0) { + startWith = lastChild(); + } + + if (startWith == 0) { + return; + } + + bool first = true; // The first layer in a stack needs special compositing + + // Fill the projection either with the cached data, or erase it. + KisFillPainter gc(m_projection); + if (adjLayer != 0) { + gc.bitBlt(rc.left(), rc.top(), + COMPOSITE_COPY, adjLayer->cachedPaintDevice(), OPACITY_OPAQUE, + rc.left(), rc.top(), rc.width(), rc.height()); + first = false; + } + else { + gc.eraseRect(rc); + first = true; + } + gc.end(); + + KisMergeVisitor visitor(m_projection, rc); + + child = startWith; + + while(child) + { + if(first) + { + // Copy the lowest layer rather than compositing it with the background + // or an empty image. This means the layer's composite op is ignored, + // which is consistent with Photoshop and gimp. + const KisCompositeOp cop = child->compositeOp(); + const bool block = child->signalsBlocked(); + child->blockSignals(true); + // Composite op copy doesn't take a mask/selection into account, so we need + // to make a difference between a paintlayer with a mask, and one without + KisPaintLayer* l = dynamic_cast(child.data()); + if (l && l->hasMask()) + child->m_compositeOp = COMPOSITE_OVER; + else + child->m_compositeOp = COMPOSITE_COPY; + child->blockSignals(block); + child->accept(visitor); + child->blockSignals(true); + child->m_compositeOp = cop; + child->blockSignals(block); + first = false; + } + else + child->accept(visitor); + + child = child->prevSibling(); + } +} + +#include "kis_group_layer.moc" diff --git a/chalk/core/kis_histogram.cc b/chalk/core/kis_histogram.cc deleted file mode 100644 index bece7c9a..00000000 --- a/chalk/core/kis_histogram.cc +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (c) 2004 Boudewijn Rempt - * (c) 2005 Bart Coppens - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include // ### Debug - -#include "kis_types.h" -#include "kis_histogram.h" -#include "kis_paint_layer.h" -#include "kis_iterators_pixel.h" -#include "kis_colorspace.h" -#include "kis_debug_areas.h" - -KisHistogram::KisHistogram(KisPaintLayerSP layer, - KisHistogramProducerSP producer, - const enumHistogramType type) -{ - m_dev = layer->paintDevice(); - m_type = type; - m_producer = producer; - m_selection = false; - m_channel = 0; - - updateHistogram(); -} - -KisHistogram::KisHistogram(KisPaintDeviceSP paintdev, - KisHistogramProducerSP producer, - const enumHistogramType type) -{ - m_dev = paintdev; - m_type = type; - m_producer = producer; - m_selection = false; - m_channel = 0; - - updateHistogram(); -} - -KisHistogram::~KisHistogram() -{ -} - -void KisHistogram::updateHistogram() -{ - TQ_INT32 x,y,w,h; - m_dev->exactBounds(x,y,w,h); - KisRectIteratorPixel srcIt = m_dev->createRectIterator(x,y,w,h, false); - KisColorSpace* cs = m_dev->colorSpace(); - - TQTime t; - t.start(); - - // Let the producer do it's work - m_producer->clear(); - int i; - // Handle degenerate case (this happens with the accumulating histogram, - // which has an empty device) - if (srcIt.isDone()) { - m_producer->addRegionToBin(0, 0, 0, cs); - } else { - while ( !srcIt.isDone() ) { - i = srcIt.nConseqPixels(); - m_producer->addRegionToBin(srcIt.rawData(), srcIt.selectionMask(), i, cs); - srcIt += i; - } - } - - computeHistogram(); -} - -void KisHistogram::computeHistogram() -{ - m_completeCalculations = calculateForRange(m_producer->viewFrom(), - m_producer->viewFrom() + m_producer->viewWidth()); - - if (m_selection) { - m_selectionCalculations = calculateForRange(m_selFrom, m_selTo); - } else { - m_selectionCalculations.clear(); - } - -#if 1 - dump(); -#endif -} - -KisHistogram::Calculations KisHistogram::calculations() { - return m_completeCalculations.at(m_channel); -} - -KisHistogram::Calculations KisHistogram::selectionCalculations() { - return m_selectionCalculations.at(m_channel); -} - -TQValueVector KisHistogram::calculateForRange(double from, double to) { - TQValueVector calculations; - uint count = m_producer->channels().count(); - - for (uint i = 0; i < count; i++) { - calculations.append(calculateSingleRange(i, from, to)); - } - - return calculations; -} - -KisHistogram::Calculations KisHistogram::calculateSingleRange(int channel, double from, double to) { - Calculations c; - - // XXX If from == to, we only want a specific bin, handle that properly! - - double max = from, min = to, total = 0.0, mean = 0.0; //, median = 0.0, stddev = 0.0; - TQ_UINT32 high = 0, low = (TQ_UINT32) -1, count = 0; - - if (m_producer->count() == 0) { - // We won't get anything, even if a range is specified - // XXX make sure all initial '0' values are correct here! - return c; - } - - TQ_INT32 totbins = m_producer->numberOfBins(); - TQ_UINT32 current; - - // convert the double range into actual bins: - double factor = static_cast(totbins) / m_producer->viewWidth(); - - TQ_INT32 fromBin = static_cast((from - m_producer->viewFrom()) * factor); - TQ_INT32 toBin = fromBin + static_cast((to - from) * factor); - - // Min, max, count, low, high - for (TQ_INT32 i = fromBin; i < toBin; i++) { - current = m_producer->getBinAt(channel, i); - double pos = static_cast(i) / factor + from; - if (current > high) - high = current; - if (current < low) - low = current; - if (current > 0) { - if (pos < min) - min = pos; - if (pos > max) - max = pos; - } - // We do the count here as well. - // we can't use m_producer->count() for this, because of the range - count += current; - total += current * pos; - } - - if (count > 0) - mean = total / count; - - c.m_high = high; - c.m_low = low; - c.m_count = count; - c.m_min = min; - c.m_max = max; - c.m_mean = mean; - c.m_total = total; - - return c; -} - - -void KisHistogram::dump() { - kdDebug(DBG_AREA_MATH) << "Histogram\n"; - - switch (m_type) { - case LINEAR: - kdDebug(DBG_AREA_MATH) << "Linear histogram\n"; - break; - case LOGARITHMIC: - kdDebug(DBG_AREA_MATH) << "Logarithmic histogram\n"; - } - - kdDebug(DBG_AREA_MATH) << "Dumping channel " << m_channel << endl; - Calculations c = calculations(); - -/* for( int i = 0; i <256; ++i ) { - kdDebug(DBG_AREA_MATH) << "Value " - << TQString().setNum(i) - << ": " - << TQString().setNum(m_values[i]) - << "\n"; - }*/ - kdDebug(DBG_AREA_MATH) << "\n"; - - kdDebug(DBG_AREA_MATH) << "Max: " << TQString(TQString().setNum(c.getMax())) << "\n"; - kdDebug(DBG_AREA_MATH) << "Min: " << TQString(TQString().setNum(c.getMin())) << "\n"; - kdDebug(DBG_AREA_MATH) << "High: " << TQString(TQString().setNum(c.getHighest())) << "\n"; - kdDebug(DBG_AREA_MATH) << "Low: " << TQString(TQString().setNum(c.getLowest())) << "\n"; - kdDebug(DBG_AREA_MATH) << "Mean: " << TQString(m_producer->positionToString(c.getMean())) << "\n"; - kdDebug(DBG_AREA_MATH) << "Total: " << TQString(TQString().setNum(c.getTotal())) << "\n"; -// kdDebug(DBG_AREA_MATH) << "Median: " << TQString().setNum(m_median) << "\n"; -// kdDebug(DBG_AREA_MATH) << "Stddev: " << TQString().setNum(m_stddev) << "\n"; -// kdDebug(DBG_AREA_MATH) << "percentile: " << TQString().setNum(m_percentile) << "\n"; - - kdDebug(DBG_AREA_MATH) << "\n"; -} diff --git a/chalk/core/kis_histogram.cpp b/chalk/core/kis_histogram.cpp new file mode 100644 index 00000000..bece7c9a --- /dev/null +++ b/chalk/core/kis_histogram.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt + * (c) 2005 Bart Coppens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include // ### Debug + +#include "kis_types.h" +#include "kis_histogram.h" +#include "kis_paint_layer.h" +#include "kis_iterators_pixel.h" +#include "kis_colorspace.h" +#include "kis_debug_areas.h" + +KisHistogram::KisHistogram(KisPaintLayerSP layer, + KisHistogramProducerSP producer, + const enumHistogramType type) +{ + m_dev = layer->paintDevice(); + m_type = type; + m_producer = producer; + m_selection = false; + m_channel = 0; + + updateHistogram(); +} + +KisHistogram::KisHistogram(KisPaintDeviceSP paintdev, + KisHistogramProducerSP producer, + const enumHistogramType type) +{ + m_dev = paintdev; + m_type = type; + m_producer = producer; + m_selection = false; + m_channel = 0; + + updateHistogram(); +} + +KisHistogram::~KisHistogram() +{ +} + +void KisHistogram::updateHistogram() +{ + TQ_INT32 x,y,w,h; + m_dev->exactBounds(x,y,w,h); + KisRectIteratorPixel srcIt = m_dev->createRectIterator(x,y,w,h, false); + KisColorSpace* cs = m_dev->colorSpace(); + + TQTime t; + t.start(); + + // Let the producer do it's work + m_producer->clear(); + int i; + // Handle degenerate case (this happens with the accumulating histogram, + // which has an empty device) + if (srcIt.isDone()) { + m_producer->addRegionToBin(0, 0, 0, cs); + } else { + while ( !srcIt.isDone() ) { + i = srcIt.nConseqPixels(); + m_producer->addRegionToBin(srcIt.rawData(), srcIt.selectionMask(), i, cs); + srcIt += i; + } + } + + computeHistogram(); +} + +void KisHistogram::computeHistogram() +{ + m_completeCalculations = calculateForRange(m_producer->viewFrom(), + m_producer->viewFrom() + m_producer->viewWidth()); + + if (m_selection) { + m_selectionCalculations = calculateForRange(m_selFrom, m_selTo); + } else { + m_selectionCalculations.clear(); + } + +#if 1 + dump(); +#endif +} + +KisHistogram::Calculations KisHistogram::calculations() { + return m_completeCalculations.at(m_channel); +} + +KisHistogram::Calculations KisHistogram::selectionCalculations() { + return m_selectionCalculations.at(m_channel); +} + +TQValueVector KisHistogram::calculateForRange(double from, double to) { + TQValueVector calculations; + uint count = m_producer->channels().count(); + + for (uint i = 0; i < count; i++) { + calculations.append(calculateSingleRange(i, from, to)); + } + + return calculations; +} + +KisHistogram::Calculations KisHistogram::calculateSingleRange(int channel, double from, double to) { + Calculations c; + + // XXX If from == to, we only want a specific bin, handle that properly! + + double max = from, min = to, total = 0.0, mean = 0.0; //, median = 0.0, stddev = 0.0; + TQ_UINT32 high = 0, low = (TQ_UINT32) -1, count = 0; + + if (m_producer->count() == 0) { + // We won't get anything, even if a range is specified + // XXX make sure all initial '0' values are correct here! + return c; + } + + TQ_INT32 totbins = m_producer->numberOfBins(); + TQ_UINT32 current; + + // convert the double range into actual bins: + double factor = static_cast(totbins) / m_producer->viewWidth(); + + TQ_INT32 fromBin = static_cast((from - m_producer->viewFrom()) * factor); + TQ_INT32 toBin = fromBin + static_cast((to - from) * factor); + + // Min, max, count, low, high + for (TQ_INT32 i = fromBin; i < toBin; i++) { + current = m_producer->getBinAt(channel, i); + double pos = static_cast(i) / factor + from; + if (current > high) + high = current; + if (current < low) + low = current; + if (current > 0) { + if (pos < min) + min = pos; + if (pos > max) + max = pos; + } + // We do the count here as well. + // we can't use m_producer->count() for this, because of the range + count += current; + total += current * pos; + } + + if (count > 0) + mean = total / count; + + c.m_high = high; + c.m_low = low; + c.m_count = count; + c.m_min = min; + c.m_max = max; + c.m_mean = mean; + c.m_total = total; + + return c; +} + + +void KisHistogram::dump() { + kdDebug(DBG_AREA_MATH) << "Histogram\n"; + + switch (m_type) { + case LINEAR: + kdDebug(DBG_AREA_MATH) << "Linear histogram\n"; + break; + case LOGARITHMIC: + kdDebug(DBG_AREA_MATH) << "Logarithmic histogram\n"; + } + + kdDebug(DBG_AREA_MATH) << "Dumping channel " << m_channel << endl; + Calculations c = calculations(); + +/* for( int i = 0; i <256; ++i ) { + kdDebug(DBG_AREA_MATH) << "Value " + << TQString().setNum(i) + << ": " + << TQString().setNum(m_values[i]) + << "\n"; + }*/ + kdDebug(DBG_AREA_MATH) << "\n"; + + kdDebug(DBG_AREA_MATH) << "Max: " << TQString(TQString().setNum(c.getMax())) << "\n"; + kdDebug(DBG_AREA_MATH) << "Min: " << TQString(TQString().setNum(c.getMin())) << "\n"; + kdDebug(DBG_AREA_MATH) << "High: " << TQString(TQString().setNum(c.getHighest())) << "\n"; + kdDebug(DBG_AREA_MATH) << "Low: " << TQString(TQString().setNum(c.getLowest())) << "\n"; + kdDebug(DBG_AREA_MATH) << "Mean: " << TQString(m_producer->positionToString(c.getMean())) << "\n"; + kdDebug(DBG_AREA_MATH) << "Total: " << TQString(TQString().setNum(c.getTotal())) << "\n"; +// kdDebug(DBG_AREA_MATH) << "Median: " << TQString().setNum(m_median) << "\n"; +// kdDebug(DBG_AREA_MATH) << "Stddev: " << TQString().setNum(m_stddev) << "\n"; +// kdDebug(DBG_AREA_MATH) << "percentile: " << TQString().setNum(m_percentile) << "\n"; + + kdDebug(DBG_AREA_MATH) << "\n"; +} diff --git a/chalk/core/kis_image.cc b/chalk/core/kis_image.cc deleted file mode 100644 index 1180bf4c..00000000 --- a/chalk/core/kis_image.cc +++ /dev/null @@ -1,1702 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * Copyright (c) 2007 Benjamin Schleimer - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include - -#include -#include LCMS_HEADER - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "kis_image_iface.h" - -#include "kis_annotation.h" -#include "kis_colorspace_factory_registry.h" -#include "kis_color.h" -#include "kis_command.h" -#include "kis_types.h" -//#include "kis_guide.h" -#include "kis_image.h" -#include "kis_paint_device.h" -#include "kis_paint_device_action.h" -#include "kis_selection.h" -#include "kis_painter.h" -#include "kis_fill_painter.h" -#include "kis_layer.h" -#include "kis_group_layer.h" -#include "kis_adjustment_layer.h" -#include "kis_paint_layer.h" -#include "kis_colorspace_convert_visitor.h" -#include "kis_background.h" -#include "kis_substrate.h" -#include "kis_scale_visitor.h" -#include "kis_nameserver.h" -#include "kis_undo_adapter.h" -#include "kis_merge_visitor.h" -#include "kis_transaction.h" -#include "kis_crop_visitor.h" -#include "kis_transform_visitor.h" -#include "kis_filter_strategy.h" -#include "kis_profile.h" -#include "kis_paint_layer.h" -#include "kis_perspective_grid.h" -#include "kis_change_profile_visitor.h" -#include "kis_group_layer.h" -#include "kis_iterators_pixel.h" -#include "kis_shear_visitor.h" - -class KisImage::KisImagePrivate { -public: - KisColor backgroundColor; - TQ_UINT32 lockCount; - bool sizeChangedWhileLocked; - bool selectionChangedWhileLocked; - KisSubstrateSP substrate; - KisPerspectiveGrid* perspectiveGrid; -}; - - -namespace { - - class KisResizeImageCmd : public KNamedCommand { - typedef KNamedCommand super; - - public: - KisResizeImageCmd(KisUndoAdapter *adapter, - KisImageSP img, - TQ_INT32 width, - TQ_INT32 height, - TQ_INT32 oldWidth, - TQ_INT32 oldHeight) : super(i18n("Resize Image")) - { - m_adapter = adapter; - m_img = img; - m_before = TQSize(oldWidth, oldHeight); - m_after = TQSize(width, height); - } - - virtual ~KisResizeImageCmd() - { - } - - public: - virtual void execute() - { - m_adapter->setUndo(false); - m_img->resize(m_after.width(), m_after.height()); - m_adapter->setUndo(true); - } - - virtual void unexecute() - { - m_adapter->setUndo(false); - m_img->resize(m_before.width(), m_before.height()); - m_adapter->setUndo(true); - } - - private: - KisUndoAdapter *m_adapter; - KisImageSP m_img; - TQSize m_before; - TQSize m_after; - }; - - // ------------------------------------------------------- - - class KisChangeLayersCmd : public KNamedCommand { - typedef KNamedCommand super; - - public: - KisChangeLayersCmd(KisUndoAdapter *adapter, KisImageSP img, - KisGroupLayerSP oldRootLayer, KisGroupLayerSP newRootLayer, const TQString& name) - : super(name) - { - m_adapter = adapter; - m_img = img; - m_oldRootLayer = oldRootLayer; - m_newRootLayer = newRootLayer; - } - - virtual ~KisChangeLayersCmd() - { - } - - public: - virtual void execute() - { - m_adapter->setUndo(false); - m_img->setRootLayer(m_newRootLayer); - m_adapter->setUndo(true); - m_img->notifyLayersChanged(); - } - - virtual void unexecute() - { - m_adapter->setUndo(false); - m_img->setRootLayer(m_oldRootLayer); - m_adapter->setUndo(true); - m_img->notifyLayersChanged(); - } - - private: - KisUndoAdapter *m_adapter; - KisImageSP m_img; - KisGroupLayerSP m_oldRootLayer; - KisGroupLayerSP m_newRootLayer; - }; - - - // ------------------------------------------------------- - - class KisConvertImageTypeCmd : public KNamedCommand { - typedef KNamedCommand super; - - public: - KisConvertImageTypeCmd(KisUndoAdapter *adapter, KisImageSP img, - KisColorSpace * beforeColorSpace, KisColorSpace * afterColorSpace - ) : super(i18n("Convert Image Type")) - { - m_adapter = adapter; - m_img = img; - m_beforeColorSpace = beforeColorSpace; - m_afterColorSpace = afterColorSpace; - } - - virtual ~KisConvertImageTypeCmd() - { - } - - public: - virtual void execute() - { - m_adapter->setUndo(false); - - m_img->setColorSpace(m_afterColorSpace); - m_img->setProfile(m_afterColorSpace->getProfile()); - - m_adapter->setUndo(true); - } - - virtual void unexecute() - { - m_adapter->setUndo(false); - - m_img->setColorSpace(m_beforeColorSpace); - m_img->setProfile(m_beforeColorSpace->getProfile()); - - m_adapter->setUndo(true); - } - - private: - KisUndoAdapter *m_adapter; - KisImageSP m_img; - KisColorSpace * m_beforeColorSpace; - KisColorSpace * m_afterColorSpace; - }; - - - // ------------------------------------------------------- - - class KisImageCommand : public KNamedCommand { - typedef KNamedCommand super; - - public: - KisImageCommand(const TQString& name, KisImageSP image); - virtual ~KisImageCommand() {} - - virtual void execute() = 0; - virtual void unexecute() = 0; - - protected: - void setUndo(bool undo); - - KisImageSP m_image; - }; - - KisImageCommand::KisImageCommand(const TQString& name, KisImageSP image) : - super(name), m_image(image) - { - } - - void KisImageCommand::setUndo(bool undo) - { - if (m_image->undoAdapter()) { - m_image->undoAdapter()->setUndo(undo); - } - } - - - // ------------------------------------------------------- - - class KisLayerPositionCommand : public KisImageCommand { - typedef KisImageCommand super; - - public: - KisLayerPositionCommand(const TQString& name, KisImageSP image, KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) : super(name, image) - { - m_layer = layer; - m_oldParent = layer->parent(); - m_oldAboveThis = layer->nextSibling(); - m_newParent = parent; - m_newAboveThis = aboveThis; - } - - virtual void execute() - { - setUndo(false); - m_image->moveLayer(m_layer, m_newParent, m_newAboveThis); - setUndo(true); - } - - virtual void unexecute() - { - setUndo(false); - m_image->moveLayer(m_layer, m_oldParent, m_oldAboveThis); - setUndo(true); - } - - private: - KisLayerSP m_layer; - KisGroupLayerSP m_oldParent; - KisLayerSP m_oldAboveThis; - KisGroupLayerSP m_newParent; - KisLayerSP m_newAboveThis; - }; - - - // ------------------------------------------------------- - - class LayerAddCmd : public KisCommand { - typedef KisCommand super; - - public: - LayerAddCmd(KisUndoAdapter *adapter, KisImageSP img, KisLayerSP layer) : super(i18n("Add Layer"), adapter) - { - m_img = img; - m_layer = layer; - m_parent = layer->parent(); - m_aboveThis = layer->nextSibling(); - } - - virtual ~LayerAddCmd() - { - } - - virtual void execute() - { - adapter()->setUndo(false); - m_img->addLayer(m_layer, m_parent.data(), m_aboveThis); - adapter()->setUndo(true); - } - - virtual void unexecute() - { - adapter()->setUndo(false); - m_img->removeLayer(m_layer); - adapter()->setUndo(true); - } - - private: - KisImageSP m_img; - KisLayerSP m_layer; - KisGroupLayerSP m_parent; - KisLayerSP m_aboveThis; - }; - - // ------------------------------------------------------- - - class LayerRmCmd : public KNamedCommand { - typedef KNamedCommand super; - - public: - LayerRmCmd(KisUndoAdapter *adapter, KisImageSP img, - KisLayerSP layer, KisGroupLayerSP wasParent, KisLayerSP wasAbove) - : super(i18n("Remove Layer")) - { - m_adapter = adapter; - m_img = img; - m_layer = layer; - m_prevParent = wasParent; - m_prevAbove = wasAbove; - } - - virtual ~LayerRmCmd() - { - } - - virtual void execute() - { - m_adapter->setUndo(false); - m_img->removeLayer(m_layer); - m_adapter->setUndo(true); - } - - virtual void unexecute() - { - m_adapter->setUndo(false); - m_img->addLayer(m_layer, m_prevParent.data(), m_prevAbove); - m_adapter->setUndo(true); - } - - private: - KisUndoAdapter *m_adapter; - KisImageSP m_img; - KisLayerSP m_layer; - KisGroupLayerSP m_prevParent; - KisLayerSP m_prevAbove; - }; - - class LayerMoveCmd: public KNamedCommand { - typedef KNamedCommand super; - - public: - LayerMoveCmd(KisUndoAdapter *adapter, KisImageSP img, - KisLayerSP layer, KisGroupLayerSP wasParent, KisLayerSP wasAbove) - : super(i18n("Move Layer")) - { - m_adapter = adapter; - m_img = img; - m_layer = layer; - m_prevParent = wasParent; - m_prevAbove = wasAbove; - m_newParent = layer->parent(); - m_newAbove = layer->nextSibling(); - } - - virtual ~LayerMoveCmd() - { - } - - virtual void execute() - { - m_adapter->setUndo(false); - m_img->moveLayer(m_layer, m_newParent.data(), m_newAbove); - m_adapter->setUndo(true); - } - - virtual void unexecute() - { - m_adapter->setUndo(false); - m_img->moveLayer(m_layer, m_prevParent.data(), m_prevAbove); - m_adapter->setUndo(true); - } - - private: - KisUndoAdapter *m_adapter; - KisImageSP m_img; - KisLayerSP m_layer; - KisGroupLayerSP m_prevParent; - KisLayerSP m_prevAbove; - KisGroupLayerSP m_newParent; - KisLayerSP m_newAbove; - }; - - - // ------------------------------------------------------- - - class LayerPropsCmd : public KNamedCommand { - typedef KNamedCommand super; - - public: - LayerPropsCmd(KisLayerSP layer, - KisImageSP img, - KisUndoAdapter *adapter, - const TQString& name, - TQ_INT32 opacity, - const KisCompositeOp& compositeOp) : super(i18n("Layer Property Changes")) - { - m_layer = layer; - m_img = img; - m_adapter = adapter; - m_name = name; - m_opacity = opacity; - m_compositeOp = compositeOp; - } - - virtual ~LayerPropsCmd() - { - } - - public: - virtual void execute() - { - TQString name = m_layer->name(); - TQ_INT32 opacity = m_layer->opacity(); - KisCompositeOp compositeOp = m_layer->compositeOp(); - - m_adapter->setUndo(false); - m_img->setLayerProperties(m_layer, - m_opacity, - m_compositeOp, - m_name); - m_adapter->setUndo(true); - m_name = name; - m_opacity = opacity; - m_compositeOp = compositeOp; - m_layer->setDirty(); - } - - virtual void unexecute() - { - execute(); - } - - private: - KisUndoAdapter *m_adapter; - KisLayerSP m_layer; - KisImageSP m_img; - TQString m_name; - TQ_INT32 m_opacity; - KisCompositeOp m_compositeOp; - }; - - // ------------------------------------------------------- - - class LockImageCommand : public KNamedCommand { - typedef KNamedCommand super; - - public: - LockImageCommand(KisImageSP img, bool lockImage) : super("lock image") // Not for translation, this - { // is only ever used inside a macro command. - m_img = img; - m_lockImage = lockImage; - } - - virtual ~LockImageCommand() - { - } - - virtual void execute() - { - if (m_lockImage) { - m_img->lock(); - } else { - m_img->unlock(); - } - } - - virtual void unexecute() - { - if (m_lockImage) { - m_img->unlock(); - } else { - m_img->lock(); - } - } - - private: - KisImageSP m_img; - bool m_lockImage; - }; -} - -KisImage::KisImage(KisUndoAdapter *adapter, TQ_INT32 width, TQ_INT32 height, KisColorSpace * colorSpace, const TQString& name) - : TQObject(0, name.latin1()), TDEShared() -{ - init(adapter, width, height, colorSpace, name); - setName(name); - m_dcop = 0L; -} - -KisImage::KisImage(const KisImage& rhs) : TQObject(), TDEShared(rhs) -{ - m_dcop = 0L; - if (this != &rhs) { - m_private = new KisImagePrivate(*rhs.m_private); - m_private->perspectiveGrid = new KisPerspectiveGrid(*rhs.m_private->perspectiveGrid); - m_uri = rhs.m_uri; - m_name = TQString(); - m_width = rhs.m_width; - m_height = rhs.m_height; - m_xres = rhs.m_xres; - m_yres = rhs.m_yres; - m_unit = rhs.m_unit; - m_colorSpace = rhs.m_colorSpace; - m_dirty = rhs.m_dirty; - m_adapter = rhs.m_adapter; - - m_bkg = new KisBackground(); - TQ_CHECK_PTR(m_bkg); - - m_rootLayer = static_cast(rhs.m_rootLayer->clone().data()); - connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); - - m_annotations = rhs.m_annotations; // XXX the annotations would probably need to be deep-copied - - m_nserver = new KisNameServer(i18n("Layer %1"), rhs.m_nserver->currentSeed() + 1); - TQ_CHECK_PTR(m_nserver); - - //m_guides = rhs.m_guides; - - // Set this as the current image for the layers - m_rootLayer->setImage(this); - // Set the active paint layer - if(rhs.activeLayer() != NULL) { - TQString layerName = rhs.activeLayer()->name(); - // kdDebug(12345) << "KisImage::KisImage: active layer = " << layerName << "\n"; - KisLayerSP activeLayer = rootLayer()->findLayer(layerName); - Q_ASSERT(activeLayer); - activate(activeLayer); - } else { - activate(NULL); - } - } -} - - - -DCOPObject * KisImage::dcopObject() -{ - if (!m_dcop) { - m_dcop = new KisImageIface(this); - TQ_CHECK_PTR(m_dcop); - } - return m_dcop; -} - -KisImage::~KisImage() -{ - delete m_private->perspectiveGrid; - delete m_private; - delete m_nserver; - delete m_dcop; -} - -TQString KisImage::name() const -{ - return m_name; -} - -void KisImage::setName(const TQString& name) -{ - if (!name.isEmpty()) - m_name = name; -} - -TQString KisImage::description() const -{ - return m_description; -} - -void KisImage::setDescription(const TQString& description) -{ - if (!description.isEmpty()) - m_description = description; -} - - -KisColor KisImage::backgroundColor() const -{ - return m_private->backgroundColor; -} - -void KisImage::setBackgroundColor(const KisColor & color) -{ - m_private->backgroundColor = color; -} - - -TQString KisImage::nextLayerName() const -{ - if (m_nserver->currentSeed() == 0) { - m_nserver->number(); - return i18n("background"); - } - - return m_nserver->name(); -} - -void KisImage::rollBackLayerName() -{ - m_nserver->rollback(); -} - -void KisImage::init(KisUndoAdapter *adapter, TQ_INT32 width, TQ_INT32 height, KisColorSpace * colorSpace, const TQString& name) -{ - Q_ASSERT(colorSpace); - - if (colorSpace == 0) { - colorSpace = KisMetaRegistry::instance()->csRegistry()->getRGB8(); - kdWarning(41010) << "No colorspace specified: using RGBA\n"; - } - - m_private = new KisImagePrivate(); - m_private->backgroundColor = KisColor(TQt::white, colorSpace); - m_private->lockCount = 0; - m_private->sizeChangedWhileLocked = false; - m_private->selectionChangedWhileLocked = false; - m_private->substrate = 0; - m_private->perspectiveGrid = new KisPerspectiveGrid(); - - m_adapter = adapter; - - m_nserver = new KisNameServer(i18n("Layer %1"), 1); - m_name = name; - - m_colorSpace = colorSpace; - m_bkg = new KisBackground(); - - m_rootLayer = new KisGroupLayer(this,"root", OPACITY_OPAQUE); - connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); - - m_xres = 1.0; - m_yres = 1.0; - m_unit = KoUnit::U_PT; - m_dirty = false; - m_width = width; - m_height = height; -} - -bool KisImage::locked() const -{ - return m_private->lockCount != 0; -} - -void KisImage::lock() -{ - if (!locked()) { - if (m_rootLayer) disconnect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); - m_private->sizeChangedWhileLocked = false; - m_private->selectionChangedWhileLocked = false; - } - m_private->lockCount++; -} - -void KisImage::unlock() -{ - Q_ASSERT(locked()); - - if (locked()) { - m_private->lockCount--; - - if (m_private->lockCount == 0) { - if (m_private->sizeChangedWhileLocked) { - // A size change implies a full image update so only send this. - emit sigSizeChanged(m_width, m_height); - } else { - if (m_rootLayer->dirty()) emit sigImageUpdated( m_rootLayer->dirtyRect() ); - } - - if (m_private->selectionChangedWhileLocked) { - emit sigActiveSelectionChanged(this); - } - - if (m_rootLayer) connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); - } - } -} - -void KisImage::emitSizeChanged() -{ - if (!locked()) { - emit sigSizeChanged(m_width, m_height); - } else { - m_private->sizeChangedWhileLocked = true; - } -} - -void KisImage::notifyLayerUpdated(KisLayerSP layer, TQRect rc) -{ - emit sigLayerUpdated(layer, rc); -} - -void KisImage::resize(TQ_INT32 w, TQ_INT32 h, TQ_INT32 x, TQ_INT32 y, bool cropLayers) -{ - if (w != width() || h != height()) { - - lock(); - - if (undo()) { - if (cropLayers) - m_adapter->beginMacro(i18n("Crop Image")); - else - m_adapter->beginMacro(i18n("Resize Image")); - - m_adapter->addCommand(new LockImageCommand(this, true)); - m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); - } - - m_width = w; - m_height = h; - - if (cropLayers) { - KisCropVisitor v(TQRect(x, y, w, h)); - m_rootLayer->accept(v); - } - - emitSizeChanged(); - - unlock(); - - if (undo()) { - m_adapter->addCommand(new LockImageCommand(this, false)); - m_adapter->endMacro(); - } - } -} - -void KisImage::resize(const TQRect& rc, bool cropLayers) -{ - resize(rc.width(), rc.height(), rc.x(), rc.y(), cropLayers); -} - - -void KisImage::scale(double sx, double sy, KisProgressDisplayInterface *progress, KisFilterStrategy *filterStrategy) -{ - if (nlayers() == 0) return; // Nothing to scale - - // New image size. XXX: Pass along to discourage rounding errors? - TQ_INT32 w, h; - w = (TQ_INT32)(( width() * sx) + 0.5); - h = (TQ_INT32)(( height() * sy) + 0.5); - - if (w != width() || h != height()) { - - lock(); - - if (undo()) { - m_adapter->beginMacro(i18n("Scale Image")); - m_adapter->addCommand(new LockImageCommand(this, true)); - } -#if 0 - if ( colorSpace()->id() == KisID("RGBA") || colorSpace()->id() == KisID("CMYK") || colorSpace()->id() == KisID("GRAYA")) { - KisScaleVisitor v (this, sx, sy, progress, filterStrategy); - m_rootLayer->accept( v ); - } - else { -#endif - KisTransformVisitor visitor (this, sx, sy, 0.0, 0.0, 0.0, 0, 0, progress, filterStrategy); - m_rootLayer->accept(visitor); -// } - - if (undo()) { - m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); - } - - m_width = w; - m_height = h; - - emitSizeChanged(); - - unlock(); - - if (undo()) { - m_adapter->addCommand(new LockImageCommand(this, false)); - m_adapter->endMacro(); - } - } -} - - - -void KisImage::rotate(double radians, KisProgressDisplayInterface *progress) -{ - lock(); - - TQ_INT32 w = width(); - TQ_INT32 h = height(); - TQ_INT32 tx = TQ_INT32((w*cos(radians) - h*sin(radians) - w) / 2 + 0.5); - TQ_INT32 ty = TQ_INT32((h*cos(radians) + w*sin(radians) - h) / 2 + 0.5); - w = (TQ_INT32)(width()*TQABS(cos(radians)) + height()*TQABS(sin(radians)) + 0.5); - h = (TQ_INT32)(height()*TQABS(cos(radians)) + width()*TQABS(sin(radians)) + 0.5); - - tx -= (w - width()) / 2; - ty -= (h - height()) / 2; - - if (undo()) { - m_adapter->beginMacro(i18n("Rotate Image")); - m_adapter->addCommand(new LockImageCommand(this, true)); - } - - KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->get(KisID("Triangle")); - KisTransformVisitor visitor (this, 1.0, 1.0, 0, 0, radians, -tx, -ty, progress, filter); - m_rootLayer->accept(visitor); - - if (undo()) m_adapter->addCommand(new KisResizeImageCmd(undoAdapter(), this, w, h, width(), height())); - - m_width = w; - m_height = h; - - emitSizeChanged(); - - unlock(); - - if (undo()) { - m_adapter->addCommand(new LockImageCommand(this, false)); - m_adapter->endMacro(); - } -} - -void KisImage::shear(double angleX, double angleY, KisProgressDisplayInterface *m_progress) -{ - const double pi=3.1415926535897932385; - - //new image size - TQ_INT32 w=width(); - TQ_INT32 h=height(); - - - if(angleX != 0 || angleY != 0){ - double deltaY=height()*TQABS(tan(angleX*pi/180)*tan(angleY*pi/180)); - w = (TQ_INT32) ( width() + TQABS(height()*tan(angleX*pi/180)) ); - //ugly fix for the problem of having two extra pixels if only a shear along one - //axis is done. This has to be fixed in the cropping code in KisRotateVisitor! - if (angleX == 0 || angleY == 0) - h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180)) ); - else if (angleX > 0 && angleY > 0) - h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180))- 2 * deltaY + 2 ); - else if (angleX < 0 && angleY < 0) - h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180))- 2 * deltaY + 2 ); - else - h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180)) ); - } - - if (w != width() || h != height()) { - - lock(); - - if (undo()) { - m_adapter->beginMacro(i18n("Shear Image")); - m_adapter->addCommand(new LockImageCommand(this, true)); - } - - KisShearVisitor v(angleX, angleY, m_progress); - v.setUndoAdapter(undoAdapter()); - rootLayer()->accept(v); - - if (undo()) m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); - - m_width = w; - m_height = h; - - emitSizeChanged(); - - unlock(); - - if (undo()) { - m_adapter->addCommand(new LockImageCommand(this, false)); - m_adapter->endMacro(); - } - } -} - -void KisImage::convertTo(KisColorSpace * dstColorSpace, TQ_INT32 renderingIntent) -{ - if ( m_colorSpace == dstColorSpace ) - { - return; - } - - lock(); - - KisColorSpace * oldCs = m_colorSpace; - - if (undo()) { - m_adapter->beginMacro(i18n("Convert Image Type")); - m_adapter->addCommand(new LockImageCommand(this, true)); - } - - setColorSpace(dstColorSpace); - - KisColorSpaceConvertVisitor visitor(dstColorSpace, renderingIntent); - m_rootLayer->accept(visitor); - - unlock(); - - emit sigLayerPropertiesChanged( m_activeLayer ); - - if (undo()) { - - m_adapter->addCommand(new KisConvertImageTypeCmd(undoAdapter(), this, - oldCs, dstColorSpace)); - m_adapter->addCommand(new LockImageCommand(this, false)); - m_adapter->endMacro(); - } -} - -KisProfile * KisImage::getProfile() const -{ - return colorSpace()->getProfile(); -} - -void KisImage::setProfile(const KisProfile * profile) -{ - if (profile == 0) return; - - KisColorSpace * dstCs= KisMetaRegistry::instance()->csRegistry()->getColorSpace( colorSpace()->id(), - profile); - if (dstCs) { - - lock(); - - KisColorSpace * oldCs = colorSpace(); - setColorSpace(dstCs); - emit(sigProfileChanged(const_cast(profile))); - - KisChangeProfileVisitor visitor(oldCs, dstCs); - m_rootLayer->accept(visitor); - - unlock(); - } -} - -double KisImage::xRes() -{ - return m_xres; -} - -double KisImage::yRes() -{ - return m_yres; -} - -void KisImage::setResolution(double xres, double yres) -{ - m_xres = xres; - m_yres = yres; -} - -TQ_INT32 KisImage::width() const -{ - return m_width; -} - -TQ_INT32 KisImage::height() const -{ - return m_height; -} - -KisPaintDeviceSP KisImage::activeDevice() -{ - if (KisPaintLayer* layer = dynamic_cast(m_activeLayer.data())) { - return layer->paintDeviceOrMask(); - } - else if (KisAdjustmentLayer* layer = dynamic_cast(m_activeLayer.data())) { - if (layer->selection()) { - return layer->selection().data(); - } - } - else if (KisGroupLayer * layer = dynamic_cast(m_activeLayer.data())) { - // Find first child - KisLayerSP child = layer->lastChild(); - while(child) - { - if (KisPaintLayer* layer = dynamic_cast(child.data())) { - return layer->paintDevice(); - } - child = child->prevSibling(); - } - KisLayerSP sibling = layer->nextSibling(); - while (sibling) { - if (KisPaintLayer* layer = dynamic_cast(sibling.data())) { - return layer->paintDevice(); - } - sibling = sibling->nextSibling(); - } - } - else if (KisLayerSP layer = m_activeLayer) { - // A weird layer -- let's not return it, but a sibling - KisLayerSP sibling = layer->nextSibling(); - while (sibling) { - if (KisPaintLayer* layer = dynamic_cast(sibling.data())) { - return layer->paintDevice(); - } - sibling = sibling->nextSibling(); - } - } - // XXX: We're buggered! - return 0; -} - -KisLayerSP KisImage::newLayer(const TQString& name, TQ_UINT8 opacity, const KisCompositeOp& compositeOp, KisColorSpace * colorstrategy) -{ - KisPaintLayer * layer; - if (colorstrategy) - layer = new KisPaintLayer(this, name, opacity, colorstrategy); - else - layer = new KisPaintLayer(this, name, opacity); - TQ_CHECK_PTR(layer); - - if (compositeOp.isValid()) - layer->setCompositeOp(compositeOp); - layer->setVisible(true); - - if (m_activeLayer != 0) { - addLayer(layer, m_activeLayer->parent().data(), m_activeLayer->nextSibling()); - } - else { - addLayer(layer, m_rootLayer, 0); - } - activate(layer); - - return layer; -} - -void KisImage::setLayerProperties(KisLayerSP layer, TQ_UINT8 opacity, const KisCompositeOp& compositeOp, const TQString& name) -{ - if (layer && (layer->opacity() != opacity || layer->compositeOp() != compositeOp || layer->name() != name)) { - if (undo()) { - TQString oldname = layer->name(); - TQ_INT32 oldopacity = layer->opacity(); - KisCompositeOp oldCompositeOp = layer->compositeOp(); - layer->setName(name); - layer->setOpacity(opacity); - layer->setCompositeOp(compositeOp); - m_adapter->addCommand(new LayerPropsCmd(layer, this, m_adapter, oldname, oldopacity, oldCompositeOp)); - } else { - layer->setName(name); - layer->setOpacity(opacity); - layer->setCompositeOp(compositeOp); - } - } -} - -KisGroupLayerSP KisImage::rootLayer() const -{ - return m_rootLayer; -} - -KisLayerSP KisImage::activeLayer() const -{ - return m_activeLayer; -} - -KisPaintDeviceSP KisImage::projection() -{ - return m_rootLayer->projection(TQRect(0, 0, m_width, m_height)); -} - -KisLayerSP KisImage::activate(KisLayerSP layer) -{ - if (layer != m_activeLayer) { - if (m_activeLayer) m_activeLayer->deactivate(); - m_activeLayer = layer; - if (m_activeLayer) m_activeLayer->activate(); - emit sigLayerActivated(m_activeLayer); - emit sigMaskInfoChanged(); - } - - return layer; -} - -KisLayerSP KisImage::findLayer(const TQString& name) const -{ - return rootLayer()->findLayer(name); -} - -KisLayerSP KisImage::findLayer(int id) const -{ - return rootLayer()->findLayer(id); -} - - -bool KisImage::addLayer(KisLayerSP layer, KisGroupLayerSP parent) -{ - return addLayer(layer, parent, parent->firstChild()); -} - -bool KisImage::addLayer(KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) -{ - if (!parent) - return false; - - const bool success = parent->addLayer(layer, aboveThis); - if (success) - { - KisPaintLayerSP player = dynamic_cast(layer.data()); - if (player != 0) { - - // XXX: This should also be done whenever a layer grows! - TQValueVector actions = KisMetaRegistry::instance() -> - csRegistry()->paintDeviceActionsFor(player->paintDevice()->colorSpace()); - for (uint i = 0; i < actions.count(); i++) { - actions.at(i)->act(player.data()->paintDevice(), width(), height()); - } - - connect(player, TQT_SIGNAL(sigMaskInfoChanged()), this, TQT_SIGNAL(sigMaskInfoChanged())); - } - - if (layer->extent().isValid()) layer->setDirty(); - - if (!layer->temporary()) { - emit sigLayerAdded(layer); - activate(layer); - } - - - if (!layer->temporary() && undo()) { - m_adapter->addCommand(new LayerAddCmd(m_adapter, this, layer)); - } - } - - return success; -} - -bool KisImage::removeLayer(KisLayerSP layer) -{ - if (!layer || layer->image() != this) - return false; - - if (KisGroupLayerSP parent = layer->parent()) { - // Adjustment layers should mark the layers underneath them, whose rendering - // they have cached, diryt on removal. Otherwise, the group won't be re-rendered. - KisAdjustmentLayer * al = dynamic_cast(layer.data()); - if (al) { - TQRect r = al->extent(); - lock(); // Lock the image, because we are going to dirty a lot of layers - KisLayerSP l = layer->nextSibling(); - while (l) { - KisAdjustmentLayer * al2 = dynamic_cast(l.data()); - l->setDirty(r, false); - if (al2 != 0) break; - l = l->nextSibling(); - } - unlock(); - } - KisPaintLayerSP player = dynamic_cast(layer.data()); - if (player != 0) { - disconnect(player, TQT_SIGNAL(sigMaskInfoChanged()), - this, TQT_SIGNAL(sigMaskInfoChanged())); - } - KisLayerSP l = layer->prevSibling(); - TQRect r = layer->extent(); - while (l) { - l->setDirty(r, false); - l = l->prevSibling(); - } - - KisLayerSP wasAbove = layer->nextSibling(); - KisLayerSP wasBelow = layer->prevSibling(); - const bool wasActive = layer == activeLayer(); - // sigLayerRemoved can set it to 0, we don't want that in the else of wasActive! - KisLayerSP actLayer = activeLayer(); - const bool success = parent->removeLayer(layer); - if (success) { - layer->setImage(0); - if (!layer->temporary() && undo()) { - m_adapter->addCommand(new LayerRmCmd(m_adapter, this, layer, parent, wasAbove)); - } - if (!layer->temporary()) { - emit sigLayerRemoved(layer, parent, wasAbove); - if (wasActive) { - if (wasBelow) - activate(wasBelow); - else if (wasAbove) - activate(wasAbove); - else if (parent != rootLayer()) - activate(parent.data()); - else - activate(rootLayer()->firstChild()); - } else { - activate(actLayer); - } - } - } - return success; - } - - return false; -} - -bool KisImage::raiseLayer(KisLayerSP layer) -{ - if (!layer) - return false; - return moveLayer(layer, layer->parent().data(), layer->prevSibling()); -} - -bool KisImage::lowerLayer(KisLayerSP layer) -{ - if (!layer) - return false; - if (KisLayerSP next = layer->nextSibling()) - return moveLayer(layer, layer->parent().data(), next->nextSibling()); - return false; -} - -bool KisImage::toTop(KisLayerSP layer) -{ - if (!layer) - return false; - return moveLayer(layer, rootLayer(), rootLayer()->firstChild()); -} - -bool KisImage::toBottom(KisLayerSP layer) -{ - if (!layer) - return false; - return moveLayer(layer, rootLayer(), 0); -} - -bool KisImage::moveLayer(KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) -{ - if (!parent) - return false; - - KisGroupLayerSP wasParent = layer->parent(); - KisLayerSP wasAbove = layer->nextSibling(); - - if (wasParent.data() == parent.data() && wasAbove.data() == aboveThis.data()) - return false; - - lock(); - - if (!wasParent->removeLayer(layer)) { - unlock(); - return false; - } - - const bool success = parent->addLayer(layer, aboveThis); - - layer->setDirty(); - - unlock(); - - if (success) - { - emit sigLayerMoved(layer, wasParent, wasAbove); - if (undo()) - m_adapter->addCommand(new LayerMoveCmd(m_adapter, this, layer, wasParent, wasAbove)); - } - else //we already removed the layer above, but re-adding it failed, so... - { - emit sigLayerRemoved(layer, wasParent, wasAbove); - if (undo()) - m_adapter->addCommand(new LayerRmCmd(m_adapter, this, layer, wasParent, wasAbove)); - } - - return success; -} - -TQ_INT32 KisImage::nlayers() const -{ - return rootLayer()->numLayers() - 1; -} - -TQ_INT32 KisImage::nHiddenLayers() const -{ - return rootLayer()->numLayers(KisLayer::Hidden); -} - -void KisImage::flatten() -{ - KisGroupLayerSP oldRootLayer = m_rootLayer; - disconnect(oldRootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); - - KisPaintLayer *dst = new KisPaintLayer(this, nextLayerName(), OPACITY_OPAQUE, colorSpace()); - TQ_CHECK_PTR(dst); - - TQRect rc = mergedImage()->extent(); - - KisPainter gc(dst->paintDevice()); - gc.bitBlt(rc.x(), rc.y(), COMPOSITE_COPY, mergedImage(), OPACITY_OPAQUE, rc.left(), rc.top(), rc.width(), rc.height()); - - m_rootLayer = new KisGroupLayer(this, "", OPACITY_OPAQUE); - connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); - - if (undo()) { - m_adapter->beginMacro(i18n("Flatten Image")); - m_adapter->addCommand(new LockImageCommand(this, true)); - m_adapter->addCommand(new KisChangeLayersCmd(m_adapter, this, oldRootLayer, m_rootLayer, "")); - } - - lock(); - - addLayer(dst, m_rootLayer, 0); - activate(dst); - - unlock(); - - notifyLayersChanged(); - - if (undo()) { - m_adapter->addCommand(new LockImageCommand(this, false)); - m_adapter->endMacro(); - } -} - - -void KisImage::mergeLayer(KisLayerSP layer) -{ - KisPaintLayer *player = new KisPaintLayer(this, layer->name(), OPACITY_OPAQUE, colorSpace()); - TQ_CHECK_PTR(player); - - TQRect rc = layer->extent() | layer->nextSibling()->extent(); - - undoAdapter()->beginMacro(i18n("Merge with Layer Below")); - - //Abuse the merge visitor to only merge two layers (if either are groups they'll recursively merge) - KisMergeVisitor visitor(player->paintDevice(), rc); - layer->nextSibling()->accept(visitor); - layer->accept(visitor); - - removeLayer(layer->nextSibling()); - addLayer(player, layer->parent(), layer); - removeLayer(layer); - - undoAdapter()->endMacro(); -} - - -void KisImage::setModified() -{ - emit sigImageModified(); -} - -void KisImage::renderToPainter(TQ_INT32 x1, - TQ_INT32 y1, - TQ_INT32 x2, - TQ_INT32 y2, - TQPainter &painter, - KisProfile * monitorProfile, - PaintFlags paintFlags, - float exposure) -{ - - TQImage img = convertToTQImage(x1, y1, x2, y2, monitorProfile, exposure); - - TQ_INT32 w = x2 - x1 + 1; - TQ_INT32 h = y2 - y1 + 1; - - - if (paintFlags & PAINT_BACKGROUND) { - m_bkg->paintBackground(img, x1, y1); - img.setAlphaBuffer(false); - } - - if (paintFlags & PAINT_SELECTION) { - if (m_activeLayer != 0) { - m_activeLayer->paintSelection(img, x1, y1, w, h); - } - } - - if (paintFlags & PAINT_MASKINACTIVELAYERS) { - if (m_activeLayer != 0) { - m_activeLayer->paintMaskInactiveLayers(img, x1, y1, w, h); - } - } - - painter.drawImage(x1, y1, img, 0, 0, w, h); -} - -TQImage KisImage::convertToTQImage(TQ_INT32 x1, - TQ_INT32 y1, - TQ_INT32 x2, - TQ_INT32 y2, - KisProfile * profile, - float exposure) -{ - TQ_INT32 w = x2 - x1 + 1; - TQ_INT32 h = y2 - y1 + 1; - - KisPaintDeviceSP dev = m_rootLayer->projection(TQRect(x1, y1, w, h)); - TQImage img = dev->convertToTQImage(profile, x1, y1, w, h, exposure); - - if (!img.isNull()) { - -#ifdef __BIG_ENDIAN__ - uchar * data = img.bits(); - for (int i = 0; i < w * h; ++i) { - uchar r, g, b, a; - a = data[0]; - b = data[1]; - g = data[2]; - r = data[3]; - data[0] = r; - data[1] = g; - data[2] = b; - data[3] = a; - data += 4; - } -#endif - - return img; - } - - return TQImage(); -} - -TQImage KisImage::convertToTQImage(const TQRect& r, const TQSize& scaledImageSize, KisProfile *profile, PaintFlags paintFlags, float exposure) -{ - - if (r.isEmpty() || scaledImageSize.isEmpty()) { - return TQImage(); - } - - TQ_INT32 imageWidth = width(); - TQ_INT32 imageHeight = height(); - TQ_UINT32 pixelSize = colorSpace()->pixelSize(); - - double xScale = static_cast(imageWidth) / scaledImageSize.width(); - double yScale = static_cast(imageHeight) / scaledImageSize.height(); - - TQRect srcRect; - - srcRect.setLeft(static_cast(r.left() * xScale)); - srcRect.setRight(static_cast(ceil((r.right() + 1) * xScale)) - 1); - srcRect.setTop(static_cast(r.top() * yScale)); - srcRect.setBottom(static_cast(ceil((r.bottom() + 1) * yScale)) - 1); - - KisPaintDeviceSP mergedImage = m_rootLayer->projection(srcRect); - TQTime t; - t.start(); - - TQ_UINT8 *scaledImageData = new TQ_UINT8[r.width() * r.height() * pixelSize]; - - TQ_UINT8 *imageRow = new TQ_UINT8[srcRect.width() * pixelSize]; - const TQ_INT32 imageRowX = srcRect.x(); - - for (TQ_INT32 y = 0; y < r.height(); ++y) { - - TQ_INT32 dstY = r.y() + y; - TQ_INT32 dstX = r.x(); - TQ_INT32 srcY = (dstY * imageHeight) / scaledImageSize.height(); - - mergedImage->readBytes(imageRow, imageRowX, srcY, srcRect.width(), 1); - - TQ_UINT8 *dstPixel = scaledImageData + (y * r.width() * pixelSize); - TQ_UINT32 columnsRemaining = r.width(); - - while (columnsRemaining > 0) { - - TQ_INT32 srcX = (dstX * imageWidth) / scaledImageSize.width(); - - memcpy(dstPixel, imageRow + ((srcX - imageRowX) * pixelSize), pixelSize); - - ++dstX; - dstPixel += pixelSize; - --columnsRemaining; - } - } - kdDebug() << "Time elapsed scaling image: " << t.elapsed() << endl; - - delete [] imageRow; - - TQImage image = colorSpace()->convertToTQImage(scaledImageData, r.width(), r.height(), profile, INTENT_PERCEPTUAL, exposure); - delete [] scaledImageData; - -#ifdef __BIG_ENDIAN__ - uchar * data = image.bits(); - for (int i = 0; i < image.width() * image.height(); ++i) { - uchar r, g, b, a; - a = data[0]; - b = data[1]; - g = data[2]; - r = data[3]; - data[0] = r; - data[1] = g; - data[2] = b; - data[3] = a; - data += 4; - } -#endif - - if (paintFlags & PAINT_BACKGROUND) { - m_bkg->paintBackground(image, r, scaledImageSize, TQSize(imageWidth, imageHeight)); - image.setAlphaBuffer(false); - } - - if (paintFlags & PAINT_SELECTION) { - if (m_activeLayer != 0) { - m_activeLayer->paintSelection(image, r, scaledImageSize, TQSize(imageWidth, imageHeight)); - } - } - - /*if (paintFlags & PAINT_MASKINACTIVELAYERS) { - if (m_activeLayer != 0) { - m_activeLayer->paintMaskInactiveLayers(img, x1, y1, w, h); - } - }*/ - - return image; -} - -KisPaintDeviceSP KisImage::mergedImage() -{ - return m_rootLayer->projection(TQRect(0, 0, m_width, m_height)); -} - -KisColor KisImage::mergedPixel(TQ_INT32 x, TQ_INT32 y) -{ - return m_rootLayer->projection(TQRect(x, y, 1, 1))->colorAt(x, y); -} - -void KisImage::notifyLayersChanged() -{ - emit sigLayersChanged(rootLayer()); -} - -void KisImage::notifyPropertyChanged(KisLayerSP layer) -{ - emit sigLayerPropertiesChanged(layer); -} - -void KisImage::notifyImageLoaded() -{ -} - -TQRect KisImage::bounds() const -{ - return TQRect(0, 0, width(), height()); -} - - -void KisImage::setUndoAdapter(KisUndoAdapter * adapter) -{ - m_adapter = adapter; -} - - -KisUndoAdapter* KisImage::undoAdapter() const -{ - return m_adapter; -} - -bool KisImage::undo() const -{ - return (m_adapter && m_adapter->undo()); -} - -//KisGuideMgr *KisImage::guides() const -//{ -// return const_cast(&m_guides); -//} - -void KisImage::slotSelectionChanged() -{ - slotSelectionChanged(bounds()); -} - -void KisImage::slotSelectionChanged(const TQRect& r) -{ - TQRect r2(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2); - - if (!locked()) { - emit sigActiveSelectionChanged(this); - emit sigSelectionChanged(this); - } else { - m_private->selectionChangedWhileLocked = true; - } -} - -KisColorSpace * KisImage::colorSpace() const -{ - return m_colorSpace; -} - -void KisImage::setColorSpace(KisColorSpace * colorSpace) -{ - m_colorSpace = colorSpace; - m_rootLayer->resetProjection(); - emit sigColorSpaceChanged(colorSpace); -} - -void KisImage::setRootLayer(KisGroupLayerSP rootLayer) -{ - disconnect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); - - m_rootLayer = rootLayer; - - if (!locked()) { - connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); - } - activate(m_rootLayer->firstChild()); -} - -void KisImage::addAnnotation(KisAnnotationSP annotation) -{ - // Find the icc annotation, if there is one - vKisAnnotationSP_it it = m_annotations.begin(); - while (it != m_annotations.end()) { - if ((*it)->type() == annotation->type()) { - *it = annotation; - return; - } - ++it; - } - m_annotations.push_back(annotation); -} - -KisAnnotationSP KisImage::annotation(TQString type) -{ - vKisAnnotationSP_it it = m_annotations.begin(); - while (it != m_annotations.end()) { - if ((*it)->type() == type) { - return *it; - } - ++it; - } - return 0; -} - -void KisImage::removeAnnotation(TQString type) -{ - vKisAnnotationSP_it it = m_annotations.begin(); - while (it != m_annotations.end()) { - if ((*it)->type() == type) { - m_annotations.erase(it); - return; - } - ++it; - } -} - -vKisAnnotationSP_it KisImage::beginAnnotations() -{ - KisProfile * profile = colorSpace()->getProfile(); - KisAnnotationSP annotation; - - if (profile) - annotation = profile->annotation(); - - if (annotation) - addAnnotation(annotation); - else - removeAnnotation("icc"); - - return m_annotations.begin(); -} - -vKisAnnotationSP_it KisImage::endAnnotations() -{ - return m_annotations.end(); -} - -KisBackgroundSP KisImage::background() const -{ - return m_bkg; -} - -KisPerspectiveGrid* KisImage::perspectiveGrid() -{ - return m_private->perspectiveGrid; -} - -#include "kis_image.moc" - diff --git a/chalk/core/kis_image.cpp b/chalk/core/kis_image.cpp new file mode 100644 index 00000000..1180bf4c --- /dev/null +++ b/chalk/core/kis_image.cpp @@ -0,0 +1,1702 @@ +/* + * Copyright (c) 2002 Patrick Julien + * Copyright (c) 2007 Benjamin Schleimer + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include + +#include +#include LCMS_HEADER + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kis_image_iface.h" + +#include "kis_annotation.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_color.h" +#include "kis_command.h" +#include "kis_types.h" +//#include "kis_guide.h" +#include "kis_image.h" +#include "kis_paint_device.h" +#include "kis_paint_device_action.h" +#include "kis_selection.h" +#include "kis_painter.h" +#include "kis_fill_painter.h" +#include "kis_layer.h" +#include "kis_group_layer.h" +#include "kis_adjustment_layer.h" +#include "kis_paint_layer.h" +#include "kis_colorspace_convert_visitor.h" +#include "kis_background.h" +#include "kis_substrate.h" +#include "kis_scale_visitor.h" +#include "kis_nameserver.h" +#include "kis_undo_adapter.h" +#include "kis_merge_visitor.h" +#include "kis_transaction.h" +#include "kis_crop_visitor.h" +#include "kis_transform_visitor.h" +#include "kis_filter_strategy.h" +#include "kis_profile.h" +#include "kis_paint_layer.h" +#include "kis_perspective_grid.h" +#include "kis_change_profile_visitor.h" +#include "kis_group_layer.h" +#include "kis_iterators_pixel.h" +#include "kis_shear_visitor.h" + +class KisImage::KisImagePrivate { +public: + KisColor backgroundColor; + TQ_UINT32 lockCount; + bool sizeChangedWhileLocked; + bool selectionChangedWhileLocked; + KisSubstrateSP substrate; + KisPerspectiveGrid* perspectiveGrid; +}; + + +namespace { + + class KisResizeImageCmd : public KNamedCommand { + typedef KNamedCommand super; + + public: + KisResizeImageCmd(KisUndoAdapter *adapter, + KisImageSP img, + TQ_INT32 width, + TQ_INT32 height, + TQ_INT32 oldWidth, + TQ_INT32 oldHeight) : super(i18n("Resize Image")) + { + m_adapter = adapter; + m_img = img; + m_before = TQSize(oldWidth, oldHeight); + m_after = TQSize(width, height); + } + + virtual ~KisResizeImageCmd() + { + } + + public: + virtual void execute() + { + m_adapter->setUndo(false); + m_img->resize(m_after.width(), m_after.height()); + m_adapter->setUndo(true); + } + + virtual void unexecute() + { + m_adapter->setUndo(false); + m_img->resize(m_before.width(), m_before.height()); + m_adapter->setUndo(true); + } + + private: + KisUndoAdapter *m_adapter; + KisImageSP m_img; + TQSize m_before; + TQSize m_after; + }; + + // ------------------------------------------------------- + + class KisChangeLayersCmd : public KNamedCommand { + typedef KNamedCommand super; + + public: + KisChangeLayersCmd(KisUndoAdapter *adapter, KisImageSP img, + KisGroupLayerSP oldRootLayer, KisGroupLayerSP newRootLayer, const TQString& name) + : super(name) + { + m_adapter = adapter; + m_img = img; + m_oldRootLayer = oldRootLayer; + m_newRootLayer = newRootLayer; + } + + virtual ~KisChangeLayersCmd() + { + } + + public: + virtual void execute() + { + m_adapter->setUndo(false); + m_img->setRootLayer(m_newRootLayer); + m_adapter->setUndo(true); + m_img->notifyLayersChanged(); + } + + virtual void unexecute() + { + m_adapter->setUndo(false); + m_img->setRootLayer(m_oldRootLayer); + m_adapter->setUndo(true); + m_img->notifyLayersChanged(); + } + + private: + KisUndoAdapter *m_adapter; + KisImageSP m_img; + KisGroupLayerSP m_oldRootLayer; + KisGroupLayerSP m_newRootLayer; + }; + + + // ------------------------------------------------------- + + class KisConvertImageTypeCmd : public KNamedCommand { + typedef KNamedCommand super; + + public: + KisConvertImageTypeCmd(KisUndoAdapter *adapter, KisImageSP img, + KisColorSpace * beforeColorSpace, KisColorSpace * afterColorSpace + ) : super(i18n("Convert Image Type")) + { + m_adapter = adapter; + m_img = img; + m_beforeColorSpace = beforeColorSpace; + m_afterColorSpace = afterColorSpace; + } + + virtual ~KisConvertImageTypeCmd() + { + } + + public: + virtual void execute() + { + m_adapter->setUndo(false); + + m_img->setColorSpace(m_afterColorSpace); + m_img->setProfile(m_afterColorSpace->getProfile()); + + m_adapter->setUndo(true); + } + + virtual void unexecute() + { + m_adapter->setUndo(false); + + m_img->setColorSpace(m_beforeColorSpace); + m_img->setProfile(m_beforeColorSpace->getProfile()); + + m_adapter->setUndo(true); + } + + private: + KisUndoAdapter *m_adapter; + KisImageSP m_img; + KisColorSpace * m_beforeColorSpace; + KisColorSpace * m_afterColorSpace; + }; + + + // ------------------------------------------------------- + + class KisImageCommand : public KNamedCommand { + typedef KNamedCommand super; + + public: + KisImageCommand(const TQString& name, KisImageSP image); + virtual ~KisImageCommand() {} + + virtual void execute() = 0; + virtual void unexecute() = 0; + + protected: + void setUndo(bool undo); + + KisImageSP m_image; + }; + + KisImageCommand::KisImageCommand(const TQString& name, KisImageSP image) : + super(name), m_image(image) + { + } + + void KisImageCommand::setUndo(bool undo) + { + if (m_image->undoAdapter()) { + m_image->undoAdapter()->setUndo(undo); + } + } + + + // ------------------------------------------------------- + + class KisLayerPositionCommand : public KisImageCommand { + typedef KisImageCommand super; + + public: + KisLayerPositionCommand(const TQString& name, KisImageSP image, KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) : super(name, image) + { + m_layer = layer; + m_oldParent = layer->parent(); + m_oldAboveThis = layer->nextSibling(); + m_newParent = parent; + m_newAboveThis = aboveThis; + } + + virtual void execute() + { + setUndo(false); + m_image->moveLayer(m_layer, m_newParent, m_newAboveThis); + setUndo(true); + } + + virtual void unexecute() + { + setUndo(false); + m_image->moveLayer(m_layer, m_oldParent, m_oldAboveThis); + setUndo(true); + } + + private: + KisLayerSP m_layer; + KisGroupLayerSP m_oldParent; + KisLayerSP m_oldAboveThis; + KisGroupLayerSP m_newParent; + KisLayerSP m_newAboveThis; + }; + + + // ------------------------------------------------------- + + class LayerAddCmd : public KisCommand { + typedef KisCommand super; + + public: + LayerAddCmd(KisUndoAdapter *adapter, KisImageSP img, KisLayerSP layer) : super(i18n("Add Layer"), adapter) + { + m_img = img; + m_layer = layer; + m_parent = layer->parent(); + m_aboveThis = layer->nextSibling(); + } + + virtual ~LayerAddCmd() + { + } + + virtual void execute() + { + adapter()->setUndo(false); + m_img->addLayer(m_layer, m_parent.data(), m_aboveThis); + adapter()->setUndo(true); + } + + virtual void unexecute() + { + adapter()->setUndo(false); + m_img->removeLayer(m_layer); + adapter()->setUndo(true); + } + + private: + KisImageSP m_img; + KisLayerSP m_layer; + KisGroupLayerSP m_parent; + KisLayerSP m_aboveThis; + }; + + // ------------------------------------------------------- + + class LayerRmCmd : public KNamedCommand { + typedef KNamedCommand super; + + public: + LayerRmCmd(KisUndoAdapter *adapter, KisImageSP img, + KisLayerSP layer, KisGroupLayerSP wasParent, KisLayerSP wasAbove) + : super(i18n("Remove Layer")) + { + m_adapter = adapter; + m_img = img; + m_layer = layer; + m_prevParent = wasParent; + m_prevAbove = wasAbove; + } + + virtual ~LayerRmCmd() + { + } + + virtual void execute() + { + m_adapter->setUndo(false); + m_img->removeLayer(m_layer); + m_adapter->setUndo(true); + } + + virtual void unexecute() + { + m_adapter->setUndo(false); + m_img->addLayer(m_layer, m_prevParent.data(), m_prevAbove); + m_adapter->setUndo(true); + } + + private: + KisUndoAdapter *m_adapter; + KisImageSP m_img; + KisLayerSP m_layer; + KisGroupLayerSP m_prevParent; + KisLayerSP m_prevAbove; + }; + + class LayerMoveCmd: public KNamedCommand { + typedef KNamedCommand super; + + public: + LayerMoveCmd(KisUndoAdapter *adapter, KisImageSP img, + KisLayerSP layer, KisGroupLayerSP wasParent, KisLayerSP wasAbove) + : super(i18n("Move Layer")) + { + m_adapter = adapter; + m_img = img; + m_layer = layer; + m_prevParent = wasParent; + m_prevAbove = wasAbove; + m_newParent = layer->parent(); + m_newAbove = layer->nextSibling(); + } + + virtual ~LayerMoveCmd() + { + } + + virtual void execute() + { + m_adapter->setUndo(false); + m_img->moveLayer(m_layer, m_newParent.data(), m_newAbove); + m_adapter->setUndo(true); + } + + virtual void unexecute() + { + m_adapter->setUndo(false); + m_img->moveLayer(m_layer, m_prevParent.data(), m_prevAbove); + m_adapter->setUndo(true); + } + + private: + KisUndoAdapter *m_adapter; + KisImageSP m_img; + KisLayerSP m_layer; + KisGroupLayerSP m_prevParent; + KisLayerSP m_prevAbove; + KisGroupLayerSP m_newParent; + KisLayerSP m_newAbove; + }; + + + // ------------------------------------------------------- + + class LayerPropsCmd : public KNamedCommand { + typedef KNamedCommand super; + + public: + LayerPropsCmd(KisLayerSP layer, + KisImageSP img, + KisUndoAdapter *adapter, + const TQString& name, + TQ_INT32 opacity, + const KisCompositeOp& compositeOp) : super(i18n("Layer Property Changes")) + { + m_layer = layer; + m_img = img; + m_adapter = adapter; + m_name = name; + m_opacity = opacity; + m_compositeOp = compositeOp; + } + + virtual ~LayerPropsCmd() + { + } + + public: + virtual void execute() + { + TQString name = m_layer->name(); + TQ_INT32 opacity = m_layer->opacity(); + KisCompositeOp compositeOp = m_layer->compositeOp(); + + m_adapter->setUndo(false); + m_img->setLayerProperties(m_layer, + m_opacity, + m_compositeOp, + m_name); + m_adapter->setUndo(true); + m_name = name; + m_opacity = opacity; + m_compositeOp = compositeOp; + m_layer->setDirty(); + } + + virtual void unexecute() + { + execute(); + } + + private: + KisUndoAdapter *m_adapter; + KisLayerSP m_layer; + KisImageSP m_img; + TQString m_name; + TQ_INT32 m_opacity; + KisCompositeOp m_compositeOp; + }; + + // ------------------------------------------------------- + + class LockImageCommand : public KNamedCommand { + typedef KNamedCommand super; + + public: + LockImageCommand(KisImageSP img, bool lockImage) : super("lock image") // Not for translation, this + { // is only ever used inside a macro command. + m_img = img; + m_lockImage = lockImage; + } + + virtual ~LockImageCommand() + { + } + + virtual void execute() + { + if (m_lockImage) { + m_img->lock(); + } else { + m_img->unlock(); + } + } + + virtual void unexecute() + { + if (m_lockImage) { + m_img->unlock(); + } else { + m_img->lock(); + } + } + + private: + KisImageSP m_img; + bool m_lockImage; + }; +} + +KisImage::KisImage(KisUndoAdapter *adapter, TQ_INT32 width, TQ_INT32 height, KisColorSpace * colorSpace, const TQString& name) + : TQObject(0, name.latin1()), TDEShared() +{ + init(adapter, width, height, colorSpace, name); + setName(name); + m_dcop = 0L; +} + +KisImage::KisImage(const KisImage& rhs) : TQObject(), TDEShared(rhs) +{ + m_dcop = 0L; + if (this != &rhs) { + m_private = new KisImagePrivate(*rhs.m_private); + m_private->perspectiveGrid = new KisPerspectiveGrid(*rhs.m_private->perspectiveGrid); + m_uri = rhs.m_uri; + m_name = TQString(); + m_width = rhs.m_width; + m_height = rhs.m_height; + m_xres = rhs.m_xres; + m_yres = rhs.m_yres; + m_unit = rhs.m_unit; + m_colorSpace = rhs.m_colorSpace; + m_dirty = rhs.m_dirty; + m_adapter = rhs.m_adapter; + + m_bkg = new KisBackground(); + TQ_CHECK_PTR(m_bkg); + + m_rootLayer = static_cast(rhs.m_rootLayer->clone().data()); + connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); + + m_annotations = rhs.m_annotations; // XXX the annotations would probably need to be deep-copied + + m_nserver = new KisNameServer(i18n("Layer %1"), rhs.m_nserver->currentSeed() + 1); + TQ_CHECK_PTR(m_nserver); + + //m_guides = rhs.m_guides; + + // Set this as the current image for the layers + m_rootLayer->setImage(this); + // Set the active paint layer + if(rhs.activeLayer() != NULL) { + TQString layerName = rhs.activeLayer()->name(); + // kdDebug(12345) << "KisImage::KisImage: active layer = " << layerName << "\n"; + KisLayerSP activeLayer = rootLayer()->findLayer(layerName); + Q_ASSERT(activeLayer); + activate(activeLayer); + } else { + activate(NULL); + } + } +} + + + +DCOPObject * KisImage::dcopObject() +{ + if (!m_dcop) { + m_dcop = new KisImageIface(this); + TQ_CHECK_PTR(m_dcop); + } + return m_dcop; +} + +KisImage::~KisImage() +{ + delete m_private->perspectiveGrid; + delete m_private; + delete m_nserver; + delete m_dcop; +} + +TQString KisImage::name() const +{ + return m_name; +} + +void KisImage::setName(const TQString& name) +{ + if (!name.isEmpty()) + m_name = name; +} + +TQString KisImage::description() const +{ + return m_description; +} + +void KisImage::setDescription(const TQString& description) +{ + if (!description.isEmpty()) + m_description = description; +} + + +KisColor KisImage::backgroundColor() const +{ + return m_private->backgroundColor; +} + +void KisImage::setBackgroundColor(const KisColor & color) +{ + m_private->backgroundColor = color; +} + + +TQString KisImage::nextLayerName() const +{ + if (m_nserver->currentSeed() == 0) { + m_nserver->number(); + return i18n("background"); + } + + return m_nserver->name(); +} + +void KisImage::rollBackLayerName() +{ + m_nserver->rollback(); +} + +void KisImage::init(KisUndoAdapter *adapter, TQ_INT32 width, TQ_INT32 height, KisColorSpace * colorSpace, const TQString& name) +{ + Q_ASSERT(colorSpace); + + if (colorSpace == 0) { + colorSpace = KisMetaRegistry::instance()->csRegistry()->getRGB8(); + kdWarning(41010) << "No colorspace specified: using RGBA\n"; + } + + m_private = new KisImagePrivate(); + m_private->backgroundColor = KisColor(TQt::white, colorSpace); + m_private->lockCount = 0; + m_private->sizeChangedWhileLocked = false; + m_private->selectionChangedWhileLocked = false; + m_private->substrate = 0; + m_private->perspectiveGrid = new KisPerspectiveGrid(); + + m_adapter = adapter; + + m_nserver = new KisNameServer(i18n("Layer %1"), 1); + m_name = name; + + m_colorSpace = colorSpace; + m_bkg = new KisBackground(); + + m_rootLayer = new KisGroupLayer(this,"root", OPACITY_OPAQUE); + connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); + + m_xres = 1.0; + m_yres = 1.0; + m_unit = KoUnit::U_PT; + m_dirty = false; + m_width = width; + m_height = height; +} + +bool KisImage::locked() const +{ + return m_private->lockCount != 0; +} + +void KisImage::lock() +{ + if (!locked()) { + if (m_rootLayer) disconnect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); + m_private->sizeChangedWhileLocked = false; + m_private->selectionChangedWhileLocked = false; + } + m_private->lockCount++; +} + +void KisImage::unlock() +{ + Q_ASSERT(locked()); + + if (locked()) { + m_private->lockCount--; + + if (m_private->lockCount == 0) { + if (m_private->sizeChangedWhileLocked) { + // A size change implies a full image update so only send this. + emit sigSizeChanged(m_width, m_height); + } else { + if (m_rootLayer->dirty()) emit sigImageUpdated( m_rootLayer->dirtyRect() ); + } + + if (m_private->selectionChangedWhileLocked) { + emit sigActiveSelectionChanged(this); + } + + if (m_rootLayer) connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); + } + } +} + +void KisImage::emitSizeChanged() +{ + if (!locked()) { + emit sigSizeChanged(m_width, m_height); + } else { + m_private->sizeChangedWhileLocked = true; + } +} + +void KisImage::notifyLayerUpdated(KisLayerSP layer, TQRect rc) +{ + emit sigLayerUpdated(layer, rc); +} + +void KisImage::resize(TQ_INT32 w, TQ_INT32 h, TQ_INT32 x, TQ_INT32 y, bool cropLayers) +{ + if (w != width() || h != height()) { + + lock(); + + if (undo()) { + if (cropLayers) + m_adapter->beginMacro(i18n("Crop Image")); + else + m_adapter->beginMacro(i18n("Resize Image")); + + m_adapter->addCommand(new LockImageCommand(this, true)); + m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); + } + + m_width = w; + m_height = h; + + if (cropLayers) { + KisCropVisitor v(TQRect(x, y, w, h)); + m_rootLayer->accept(v); + } + + emitSizeChanged(); + + unlock(); + + if (undo()) { + m_adapter->addCommand(new LockImageCommand(this, false)); + m_adapter->endMacro(); + } + } +} + +void KisImage::resize(const TQRect& rc, bool cropLayers) +{ + resize(rc.width(), rc.height(), rc.x(), rc.y(), cropLayers); +} + + +void KisImage::scale(double sx, double sy, KisProgressDisplayInterface *progress, KisFilterStrategy *filterStrategy) +{ + if (nlayers() == 0) return; // Nothing to scale + + // New image size. XXX: Pass along to discourage rounding errors? + TQ_INT32 w, h; + w = (TQ_INT32)(( width() * sx) + 0.5); + h = (TQ_INT32)(( height() * sy) + 0.5); + + if (w != width() || h != height()) { + + lock(); + + if (undo()) { + m_adapter->beginMacro(i18n("Scale Image")); + m_adapter->addCommand(new LockImageCommand(this, true)); + } +#if 0 + if ( colorSpace()->id() == KisID("RGBA") || colorSpace()->id() == KisID("CMYK") || colorSpace()->id() == KisID("GRAYA")) { + KisScaleVisitor v (this, sx, sy, progress, filterStrategy); + m_rootLayer->accept( v ); + } + else { +#endif + KisTransformVisitor visitor (this, sx, sy, 0.0, 0.0, 0.0, 0, 0, progress, filterStrategy); + m_rootLayer->accept(visitor); +// } + + if (undo()) { + m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); + } + + m_width = w; + m_height = h; + + emitSizeChanged(); + + unlock(); + + if (undo()) { + m_adapter->addCommand(new LockImageCommand(this, false)); + m_adapter->endMacro(); + } + } +} + + + +void KisImage::rotate(double radians, KisProgressDisplayInterface *progress) +{ + lock(); + + TQ_INT32 w = width(); + TQ_INT32 h = height(); + TQ_INT32 tx = TQ_INT32((w*cos(radians) - h*sin(radians) - w) / 2 + 0.5); + TQ_INT32 ty = TQ_INT32((h*cos(radians) + w*sin(radians) - h) / 2 + 0.5); + w = (TQ_INT32)(width()*TQABS(cos(radians)) + height()*TQABS(sin(radians)) + 0.5); + h = (TQ_INT32)(height()*TQABS(cos(radians)) + width()*TQABS(sin(radians)) + 0.5); + + tx -= (w - width()) / 2; + ty -= (h - height()) / 2; + + if (undo()) { + m_adapter->beginMacro(i18n("Rotate Image")); + m_adapter->addCommand(new LockImageCommand(this, true)); + } + + KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->get(KisID("Triangle")); + KisTransformVisitor visitor (this, 1.0, 1.0, 0, 0, radians, -tx, -ty, progress, filter); + m_rootLayer->accept(visitor); + + if (undo()) m_adapter->addCommand(new KisResizeImageCmd(undoAdapter(), this, w, h, width(), height())); + + m_width = w; + m_height = h; + + emitSizeChanged(); + + unlock(); + + if (undo()) { + m_adapter->addCommand(new LockImageCommand(this, false)); + m_adapter->endMacro(); + } +} + +void KisImage::shear(double angleX, double angleY, KisProgressDisplayInterface *m_progress) +{ + const double pi=3.1415926535897932385; + + //new image size + TQ_INT32 w=width(); + TQ_INT32 h=height(); + + + if(angleX != 0 || angleY != 0){ + double deltaY=height()*TQABS(tan(angleX*pi/180)*tan(angleY*pi/180)); + w = (TQ_INT32) ( width() + TQABS(height()*tan(angleX*pi/180)) ); + //ugly fix for the problem of having two extra pixels if only a shear along one + //axis is done. This has to be fixed in the cropping code in KisRotateVisitor! + if (angleX == 0 || angleY == 0) + h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180)) ); + else if (angleX > 0 && angleY > 0) + h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180))- 2 * deltaY + 2 ); + else if (angleX < 0 && angleY < 0) + h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180))- 2 * deltaY + 2 ); + else + h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180)) ); + } + + if (w != width() || h != height()) { + + lock(); + + if (undo()) { + m_adapter->beginMacro(i18n("Shear Image")); + m_adapter->addCommand(new LockImageCommand(this, true)); + } + + KisShearVisitor v(angleX, angleY, m_progress); + v.setUndoAdapter(undoAdapter()); + rootLayer()->accept(v); + + if (undo()) m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); + + m_width = w; + m_height = h; + + emitSizeChanged(); + + unlock(); + + if (undo()) { + m_adapter->addCommand(new LockImageCommand(this, false)); + m_adapter->endMacro(); + } + } +} + +void KisImage::convertTo(KisColorSpace * dstColorSpace, TQ_INT32 renderingIntent) +{ + if ( m_colorSpace == dstColorSpace ) + { + return; + } + + lock(); + + KisColorSpace * oldCs = m_colorSpace; + + if (undo()) { + m_adapter->beginMacro(i18n("Convert Image Type")); + m_adapter->addCommand(new LockImageCommand(this, true)); + } + + setColorSpace(dstColorSpace); + + KisColorSpaceConvertVisitor visitor(dstColorSpace, renderingIntent); + m_rootLayer->accept(visitor); + + unlock(); + + emit sigLayerPropertiesChanged( m_activeLayer ); + + if (undo()) { + + m_adapter->addCommand(new KisConvertImageTypeCmd(undoAdapter(), this, + oldCs, dstColorSpace)); + m_adapter->addCommand(new LockImageCommand(this, false)); + m_adapter->endMacro(); + } +} + +KisProfile * KisImage::getProfile() const +{ + return colorSpace()->getProfile(); +} + +void KisImage::setProfile(const KisProfile * profile) +{ + if (profile == 0) return; + + KisColorSpace * dstCs= KisMetaRegistry::instance()->csRegistry()->getColorSpace( colorSpace()->id(), + profile); + if (dstCs) { + + lock(); + + KisColorSpace * oldCs = colorSpace(); + setColorSpace(dstCs); + emit(sigProfileChanged(const_cast(profile))); + + KisChangeProfileVisitor visitor(oldCs, dstCs); + m_rootLayer->accept(visitor); + + unlock(); + } +} + +double KisImage::xRes() +{ + return m_xres; +} + +double KisImage::yRes() +{ + return m_yres; +} + +void KisImage::setResolution(double xres, double yres) +{ + m_xres = xres; + m_yres = yres; +} + +TQ_INT32 KisImage::width() const +{ + return m_width; +} + +TQ_INT32 KisImage::height() const +{ + return m_height; +} + +KisPaintDeviceSP KisImage::activeDevice() +{ + if (KisPaintLayer* layer = dynamic_cast(m_activeLayer.data())) { + return layer->paintDeviceOrMask(); + } + else if (KisAdjustmentLayer* layer = dynamic_cast(m_activeLayer.data())) { + if (layer->selection()) { + return layer->selection().data(); + } + } + else if (KisGroupLayer * layer = dynamic_cast(m_activeLayer.data())) { + // Find first child + KisLayerSP child = layer->lastChild(); + while(child) + { + if (KisPaintLayer* layer = dynamic_cast(child.data())) { + return layer->paintDevice(); + } + child = child->prevSibling(); + } + KisLayerSP sibling = layer->nextSibling(); + while (sibling) { + if (KisPaintLayer* layer = dynamic_cast(sibling.data())) { + return layer->paintDevice(); + } + sibling = sibling->nextSibling(); + } + } + else if (KisLayerSP layer = m_activeLayer) { + // A weird layer -- let's not return it, but a sibling + KisLayerSP sibling = layer->nextSibling(); + while (sibling) { + if (KisPaintLayer* layer = dynamic_cast(sibling.data())) { + return layer->paintDevice(); + } + sibling = sibling->nextSibling(); + } + } + // XXX: We're buggered! + return 0; +} + +KisLayerSP KisImage::newLayer(const TQString& name, TQ_UINT8 opacity, const KisCompositeOp& compositeOp, KisColorSpace * colorstrategy) +{ + KisPaintLayer * layer; + if (colorstrategy) + layer = new KisPaintLayer(this, name, opacity, colorstrategy); + else + layer = new KisPaintLayer(this, name, opacity); + TQ_CHECK_PTR(layer); + + if (compositeOp.isValid()) + layer->setCompositeOp(compositeOp); + layer->setVisible(true); + + if (m_activeLayer != 0) { + addLayer(layer, m_activeLayer->parent().data(), m_activeLayer->nextSibling()); + } + else { + addLayer(layer, m_rootLayer, 0); + } + activate(layer); + + return layer; +} + +void KisImage::setLayerProperties(KisLayerSP layer, TQ_UINT8 opacity, const KisCompositeOp& compositeOp, const TQString& name) +{ + if (layer && (layer->opacity() != opacity || layer->compositeOp() != compositeOp || layer->name() != name)) { + if (undo()) { + TQString oldname = layer->name(); + TQ_INT32 oldopacity = layer->opacity(); + KisCompositeOp oldCompositeOp = layer->compositeOp(); + layer->setName(name); + layer->setOpacity(opacity); + layer->setCompositeOp(compositeOp); + m_adapter->addCommand(new LayerPropsCmd(layer, this, m_adapter, oldname, oldopacity, oldCompositeOp)); + } else { + layer->setName(name); + layer->setOpacity(opacity); + layer->setCompositeOp(compositeOp); + } + } +} + +KisGroupLayerSP KisImage::rootLayer() const +{ + return m_rootLayer; +} + +KisLayerSP KisImage::activeLayer() const +{ + return m_activeLayer; +} + +KisPaintDeviceSP KisImage::projection() +{ + return m_rootLayer->projection(TQRect(0, 0, m_width, m_height)); +} + +KisLayerSP KisImage::activate(KisLayerSP layer) +{ + if (layer != m_activeLayer) { + if (m_activeLayer) m_activeLayer->deactivate(); + m_activeLayer = layer; + if (m_activeLayer) m_activeLayer->activate(); + emit sigLayerActivated(m_activeLayer); + emit sigMaskInfoChanged(); + } + + return layer; +} + +KisLayerSP KisImage::findLayer(const TQString& name) const +{ + return rootLayer()->findLayer(name); +} + +KisLayerSP KisImage::findLayer(int id) const +{ + return rootLayer()->findLayer(id); +} + + +bool KisImage::addLayer(KisLayerSP layer, KisGroupLayerSP parent) +{ + return addLayer(layer, parent, parent->firstChild()); +} + +bool KisImage::addLayer(KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) +{ + if (!parent) + return false; + + const bool success = parent->addLayer(layer, aboveThis); + if (success) + { + KisPaintLayerSP player = dynamic_cast(layer.data()); + if (player != 0) { + + // XXX: This should also be done whenever a layer grows! + TQValueVector actions = KisMetaRegistry::instance() -> + csRegistry()->paintDeviceActionsFor(player->paintDevice()->colorSpace()); + for (uint i = 0; i < actions.count(); i++) { + actions.at(i)->act(player.data()->paintDevice(), width(), height()); + } + + connect(player, TQT_SIGNAL(sigMaskInfoChanged()), this, TQT_SIGNAL(sigMaskInfoChanged())); + } + + if (layer->extent().isValid()) layer->setDirty(); + + if (!layer->temporary()) { + emit sigLayerAdded(layer); + activate(layer); + } + + + if (!layer->temporary() && undo()) { + m_adapter->addCommand(new LayerAddCmd(m_adapter, this, layer)); + } + } + + return success; +} + +bool KisImage::removeLayer(KisLayerSP layer) +{ + if (!layer || layer->image() != this) + return false; + + if (KisGroupLayerSP parent = layer->parent()) { + // Adjustment layers should mark the layers underneath them, whose rendering + // they have cached, diryt on removal. Otherwise, the group won't be re-rendered. + KisAdjustmentLayer * al = dynamic_cast(layer.data()); + if (al) { + TQRect r = al->extent(); + lock(); // Lock the image, because we are going to dirty a lot of layers + KisLayerSP l = layer->nextSibling(); + while (l) { + KisAdjustmentLayer * al2 = dynamic_cast(l.data()); + l->setDirty(r, false); + if (al2 != 0) break; + l = l->nextSibling(); + } + unlock(); + } + KisPaintLayerSP player = dynamic_cast(layer.data()); + if (player != 0) { + disconnect(player, TQT_SIGNAL(sigMaskInfoChanged()), + this, TQT_SIGNAL(sigMaskInfoChanged())); + } + KisLayerSP l = layer->prevSibling(); + TQRect r = layer->extent(); + while (l) { + l->setDirty(r, false); + l = l->prevSibling(); + } + + KisLayerSP wasAbove = layer->nextSibling(); + KisLayerSP wasBelow = layer->prevSibling(); + const bool wasActive = layer == activeLayer(); + // sigLayerRemoved can set it to 0, we don't want that in the else of wasActive! + KisLayerSP actLayer = activeLayer(); + const bool success = parent->removeLayer(layer); + if (success) { + layer->setImage(0); + if (!layer->temporary() && undo()) { + m_adapter->addCommand(new LayerRmCmd(m_adapter, this, layer, parent, wasAbove)); + } + if (!layer->temporary()) { + emit sigLayerRemoved(layer, parent, wasAbove); + if (wasActive) { + if (wasBelow) + activate(wasBelow); + else if (wasAbove) + activate(wasAbove); + else if (parent != rootLayer()) + activate(parent.data()); + else + activate(rootLayer()->firstChild()); + } else { + activate(actLayer); + } + } + } + return success; + } + + return false; +} + +bool KisImage::raiseLayer(KisLayerSP layer) +{ + if (!layer) + return false; + return moveLayer(layer, layer->parent().data(), layer->prevSibling()); +} + +bool KisImage::lowerLayer(KisLayerSP layer) +{ + if (!layer) + return false; + if (KisLayerSP next = layer->nextSibling()) + return moveLayer(layer, layer->parent().data(), next->nextSibling()); + return false; +} + +bool KisImage::toTop(KisLayerSP layer) +{ + if (!layer) + return false; + return moveLayer(layer, rootLayer(), rootLayer()->firstChild()); +} + +bool KisImage::toBottom(KisLayerSP layer) +{ + if (!layer) + return false; + return moveLayer(layer, rootLayer(), 0); +} + +bool KisImage::moveLayer(KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) +{ + if (!parent) + return false; + + KisGroupLayerSP wasParent = layer->parent(); + KisLayerSP wasAbove = layer->nextSibling(); + + if (wasParent.data() == parent.data() && wasAbove.data() == aboveThis.data()) + return false; + + lock(); + + if (!wasParent->removeLayer(layer)) { + unlock(); + return false; + } + + const bool success = parent->addLayer(layer, aboveThis); + + layer->setDirty(); + + unlock(); + + if (success) + { + emit sigLayerMoved(layer, wasParent, wasAbove); + if (undo()) + m_adapter->addCommand(new LayerMoveCmd(m_adapter, this, layer, wasParent, wasAbove)); + } + else //we already removed the layer above, but re-adding it failed, so... + { + emit sigLayerRemoved(layer, wasParent, wasAbove); + if (undo()) + m_adapter->addCommand(new LayerRmCmd(m_adapter, this, layer, wasParent, wasAbove)); + } + + return success; +} + +TQ_INT32 KisImage::nlayers() const +{ + return rootLayer()->numLayers() - 1; +} + +TQ_INT32 KisImage::nHiddenLayers() const +{ + return rootLayer()->numLayers(KisLayer::Hidden); +} + +void KisImage::flatten() +{ + KisGroupLayerSP oldRootLayer = m_rootLayer; + disconnect(oldRootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); + + KisPaintLayer *dst = new KisPaintLayer(this, nextLayerName(), OPACITY_OPAQUE, colorSpace()); + TQ_CHECK_PTR(dst); + + TQRect rc = mergedImage()->extent(); + + KisPainter gc(dst->paintDevice()); + gc.bitBlt(rc.x(), rc.y(), COMPOSITE_COPY, mergedImage(), OPACITY_OPAQUE, rc.left(), rc.top(), rc.width(), rc.height()); + + m_rootLayer = new KisGroupLayer(this, "", OPACITY_OPAQUE); + connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); + + if (undo()) { + m_adapter->beginMacro(i18n("Flatten Image")); + m_adapter->addCommand(new LockImageCommand(this, true)); + m_adapter->addCommand(new KisChangeLayersCmd(m_adapter, this, oldRootLayer, m_rootLayer, "")); + } + + lock(); + + addLayer(dst, m_rootLayer, 0); + activate(dst); + + unlock(); + + notifyLayersChanged(); + + if (undo()) { + m_adapter->addCommand(new LockImageCommand(this, false)); + m_adapter->endMacro(); + } +} + + +void KisImage::mergeLayer(KisLayerSP layer) +{ + KisPaintLayer *player = new KisPaintLayer(this, layer->name(), OPACITY_OPAQUE, colorSpace()); + TQ_CHECK_PTR(player); + + TQRect rc = layer->extent() | layer->nextSibling()->extent(); + + undoAdapter()->beginMacro(i18n("Merge with Layer Below")); + + //Abuse the merge visitor to only merge two layers (if either are groups they'll recursively merge) + KisMergeVisitor visitor(player->paintDevice(), rc); + layer->nextSibling()->accept(visitor); + layer->accept(visitor); + + removeLayer(layer->nextSibling()); + addLayer(player, layer->parent(), layer); + removeLayer(layer); + + undoAdapter()->endMacro(); +} + + +void KisImage::setModified() +{ + emit sigImageModified(); +} + +void KisImage::renderToPainter(TQ_INT32 x1, + TQ_INT32 y1, + TQ_INT32 x2, + TQ_INT32 y2, + TQPainter &painter, + KisProfile * monitorProfile, + PaintFlags paintFlags, + float exposure) +{ + + TQImage img = convertToTQImage(x1, y1, x2, y2, monitorProfile, exposure); + + TQ_INT32 w = x2 - x1 + 1; + TQ_INT32 h = y2 - y1 + 1; + + + if (paintFlags & PAINT_BACKGROUND) { + m_bkg->paintBackground(img, x1, y1); + img.setAlphaBuffer(false); + } + + if (paintFlags & PAINT_SELECTION) { + if (m_activeLayer != 0) { + m_activeLayer->paintSelection(img, x1, y1, w, h); + } + } + + if (paintFlags & PAINT_MASKINACTIVELAYERS) { + if (m_activeLayer != 0) { + m_activeLayer->paintMaskInactiveLayers(img, x1, y1, w, h); + } + } + + painter.drawImage(x1, y1, img, 0, 0, w, h); +} + +TQImage KisImage::convertToTQImage(TQ_INT32 x1, + TQ_INT32 y1, + TQ_INT32 x2, + TQ_INT32 y2, + KisProfile * profile, + float exposure) +{ + TQ_INT32 w = x2 - x1 + 1; + TQ_INT32 h = y2 - y1 + 1; + + KisPaintDeviceSP dev = m_rootLayer->projection(TQRect(x1, y1, w, h)); + TQImage img = dev->convertToTQImage(profile, x1, y1, w, h, exposure); + + if (!img.isNull()) { + +#ifdef __BIG_ENDIAN__ + uchar * data = img.bits(); + for (int i = 0; i < w * h; ++i) { + uchar r, g, b, a; + a = data[0]; + b = data[1]; + g = data[2]; + r = data[3]; + data[0] = r; + data[1] = g; + data[2] = b; + data[3] = a; + data += 4; + } +#endif + + return img; + } + + return TQImage(); +} + +TQImage KisImage::convertToTQImage(const TQRect& r, const TQSize& scaledImageSize, KisProfile *profile, PaintFlags paintFlags, float exposure) +{ + + if (r.isEmpty() || scaledImageSize.isEmpty()) { + return TQImage(); + } + + TQ_INT32 imageWidth = width(); + TQ_INT32 imageHeight = height(); + TQ_UINT32 pixelSize = colorSpace()->pixelSize(); + + double xScale = static_cast(imageWidth) / scaledImageSize.width(); + double yScale = static_cast(imageHeight) / scaledImageSize.height(); + + TQRect srcRect; + + srcRect.setLeft(static_cast(r.left() * xScale)); + srcRect.setRight(static_cast(ceil((r.right() + 1) * xScale)) - 1); + srcRect.setTop(static_cast(r.top() * yScale)); + srcRect.setBottom(static_cast(ceil((r.bottom() + 1) * yScale)) - 1); + + KisPaintDeviceSP mergedImage = m_rootLayer->projection(srcRect); + TQTime t; + t.start(); + + TQ_UINT8 *scaledImageData = new TQ_UINT8[r.width() * r.height() * pixelSize]; + + TQ_UINT8 *imageRow = new TQ_UINT8[srcRect.width() * pixelSize]; + const TQ_INT32 imageRowX = srcRect.x(); + + for (TQ_INT32 y = 0; y < r.height(); ++y) { + + TQ_INT32 dstY = r.y() + y; + TQ_INT32 dstX = r.x(); + TQ_INT32 srcY = (dstY * imageHeight) / scaledImageSize.height(); + + mergedImage->readBytes(imageRow, imageRowX, srcY, srcRect.width(), 1); + + TQ_UINT8 *dstPixel = scaledImageData + (y * r.width() * pixelSize); + TQ_UINT32 columnsRemaining = r.width(); + + while (columnsRemaining > 0) { + + TQ_INT32 srcX = (dstX * imageWidth) / scaledImageSize.width(); + + memcpy(dstPixel, imageRow + ((srcX - imageRowX) * pixelSize), pixelSize); + + ++dstX; + dstPixel += pixelSize; + --columnsRemaining; + } + } + kdDebug() << "Time elapsed scaling image: " << t.elapsed() << endl; + + delete [] imageRow; + + TQImage image = colorSpace()->convertToTQImage(scaledImageData, r.width(), r.height(), profile, INTENT_PERCEPTUAL, exposure); + delete [] scaledImageData; + +#ifdef __BIG_ENDIAN__ + uchar * data = image.bits(); + for (int i = 0; i < image.width() * image.height(); ++i) { + uchar r, g, b, a; + a = data[0]; + b = data[1]; + g = data[2]; + r = data[3]; + data[0] = r; + data[1] = g; + data[2] = b; + data[3] = a; + data += 4; + } +#endif + + if (paintFlags & PAINT_BACKGROUND) { + m_bkg->paintBackground(image, r, scaledImageSize, TQSize(imageWidth, imageHeight)); + image.setAlphaBuffer(false); + } + + if (paintFlags & PAINT_SELECTION) { + if (m_activeLayer != 0) { + m_activeLayer->paintSelection(image, r, scaledImageSize, TQSize(imageWidth, imageHeight)); + } + } + + /*if (paintFlags & PAINT_MASKINACTIVELAYERS) { + if (m_activeLayer != 0) { + m_activeLayer->paintMaskInactiveLayers(img, x1, y1, w, h); + } + }*/ + + return image; +} + +KisPaintDeviceSP KisImage::mergedImage() +{ + return m_rootLayer->projection(TQRect(0, 0, m_width, m_height)); +} + +KisColor KisImage::mergedPixel(TQ_INT32 x, TQ_INT32 y) +{ + return m_rootLayer->projection(TQRect(x, y, 1, 1))->colorAt(x, y); +} + +void KisImage::notifyLayersChanged() +{ + emit sigLayersChanged(rootLayer()); +} + +void KisImage::notifyPropertyChanged(KisLayerSP layer) +{ + emit sigLayerPropertiesChanged(layer); +} + +void KisImage::notifyImageLoaded() +{ +} + +TQRect KisImage::bounds() const +{ + return TQRect(0, 0, width(), height()); +} + + +void KisImage::setUndoAdapter(KisUndoAdapter * adapter) +{ + m_adapter = adapter; +} + + +KisUndoAdapter* KisImage::undoAdapter() const +{ + return m_adapter; +} + +bool KisImage::undo() const +{ + return (m_adapter && m_adapter->undo()); +} + +//KisGuideMgr *KisImage::guides() const +//{ +// return const_cast(&m_guides); +//} + +void KisImage::slotSelectionChanged() +{ + slotSelectionChanged(bounds()); +} + +void KisImage::slotSelectionChanged(const TQRect& r) +{ + TQRect r2(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2); + + if (!locked()) { + emit sigActiveSelectionChanged(this); + emit sigSelectionChanged(this); + } else { + m_private->selectionChangedWhileLocked = true; + } +} + +KisColorSpace * KisImage::colorSpace() const +{ + return m_colorSpace; +} + +void KisImage::setColorSpace(KisColorSpace * colorSpace) +{ + m_colorSpace = colorSpace; + m_rootLayer->resetProjection(); + emit sigColorSpaceChanged(colorSpace); +} + +void KisImage::setRootLayer(KisGroupLayerSP rootLayer) +{ + disconnect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); + + m_rootLayer = rootLayer; + + if (!locked()) { + connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect))); + } + activate(m_rootLayer->firstChild()); +} + +void KisImage::addAnnotation(KisAnnotationSP annotation) +{ + // Find the icc annotation, if there is one + vKisAnnotationSP_it it = m_annotations.begin(); + while (it != m_annotations.end()) { + if ((*it)->type() == annotation->type()) { + *it = annotation; + return; + } + ++it; + } + m_annotations.push_back(annotation); +} + +KisAnnotationSP KisImage::annotation(TQString type) +{ + vKisAnnotationSP_it it = m_annotations.begin(); + while (it != m_annotations.end()) { + if ((*it)->type() == type) { + return *it; + } + ++it; + } + return 0; +} + +void KisImage::removeAnnotation(TQString type) +{ + vKisAnnotationSP_it it = m_annotations.begin(); + while (it != m_annotations.end()) { + if ((*it)->type() == type) { + m_annotations.erase(it); + return; + } + ++it; + } +} + +vKisAnnotationSP_it KisImage::beginAnnotations() +{ + KisProfile * profile = colorSpace()->getProfile(); + KisAnnotationSP annotation; + + if (profile) + annotation = profile->annotation(); + + if (annotation) + addAnnotation(annotation); + else + removeAnnotation("icc"); + + return m_annotations.begin(); +} + +vKisAnnotationSP_it KisImage::endAnnotations() +{ + return m_annotations.end(); +} + +KisBackgroundSP KisImage::background() const +{ + return m_bkg; +} + +KisPerspectiveGrid* KisImage::perspectiveGrid() +{ + return m_private->perspectiveGrid; +} + +#include "kis_image.moc" + diff --git a/chalk/core/kis_image_iface.cc b/chalk/core/kis_image_iface.cc deleted file mode 100644 index 2cc16e27..00000000 --- a/chalk/core/kis_image_iface.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This file is part of the KDE project - * - * Copyright (C) 2002 Laurent Montel - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -#include - -#include "kis_image_iface.h" -#include "kis_types.h" -#include "kis_image.h" -#include "kis_paint_device.h" -#include "kis_paint_device_iface.h" -#include - -KisImageIface::KisImageIface( KisImage * img ) - : DCOPObject(img->name().utf8()) -{ - m_img = img; -} - -int KisImageIface::height() const -{ - return m_img->height(); -} - -int KisImageIface::width() const -{ - return m_img->width(); -} - -void KisImageIface::setName(const TQString& name) -{ - m_img->setName( name ); -} - -void KisImageIface::rotateCCW() -{ - // XXX: Add progress display if there is a view - m_img->rotate(270, 0); -} - -void KisImageIface::rotateCW() -{ - // XXX: Add progressdisplay if there is a view - m_img->rotate(90, 0); -} - -void KisImageIface::rotate180() -{ - // XXX: Add progressdisplay if there is a view - m_img->rotate(180, 0); -} - -void KisImageIface::rotate(double angle) -{ - // XXX: Add progressdisplay if there is a view - angle *= M_PI/180; - m_img->rotate(angle, 0); -} - -DCOPRef KisImageIface::activeDevice() -{ - KisPaintDeviceSP dev = m_img->activeDevice(); - - if( !dev ) - return DCOPRef(); - else - return DCOPRef( kapp->dcopClient()->appId(), - dev->dcopObject()->objId(), - "KisPaintDeviceIface"); - -} - -DCOPRef KisImageIface::colorSpace() const -{ - KisColorSpace * cs = m_img->colorSpace(); - if ( !cs ) - return DCOPRef(); - else - return DCOPRef( kapp->dcopClient()->appId(), - cs->dcopObject()->objId(), - "KisColorSpaceIface" ); -} diff --git a/chalk/core/kis_image_iface.cpp b/chalk/core/kis_image_iface.cpp new file mode 100644 index 00000000..2cc16e27 --- /dev/null +++ b/chalk/core/kis_image_iface.cpp @@ -0,0 +1,97 @@ +/* + * This file is part of the KDE project + * + * Copyright (C) 2002 Laurent Montel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include + +#include "kis_image_iface.h" +#include "kis_types.h" +#include "kis_image.h" +#include "kis_paint_device.h" +#include "kis_paint_device_iface.h" +#include + +KisImageIface::KisImageIface( KisImage * img ) + : DCOPObject(img->name().utf8()) +{ + m_img = img; +} + +int KisImageIface::height() const +{ + return m_img->height(); +} + +int KisImageIface::width() const +{ + return m_img->width(); +} + +void KisImageIface::setName(const TQString& name) +{ + m_img->setName( name ); +} + +void KisImageIface::rotateCCW() +{ + // XXX: Add progress display if there is a view + m_img->rotate(270, 0); +} + +void KisImageIface::rotateCW() +{ + // XXX: Add progressdisplay if there is a view + m_img->rotate(90, 0); +} + +void KisImageIface::rotate180() +{ + // XXX: Add progressdisplay if there is a view + m_img->rotate(180, 0); +} + +void KisImageIface::rotate(double angle) +{ + // XXX: Add progressdisplay if there is a view + angle *= M_PI/180; + m_img->rotate(angle, 0); +} + +DCOPRef KisImageIface::activeDevice() +{ + KisPaintDeviceSP dev = m_img->activeDevice(); + + if( !dev ) + return DCOPRef(); + else + return DCOPRef( kapp->dcopClient()->appId(), + dev->dcopObject()->objId(), + "KisPaintDeviceIface"); + +} + +DCOPRef KisImageIface::colorSpace() const +{ + KisColorSpace * cs = m_img->colorSpace(); + if ( !cs ) + return DCOPRef(); + else + return DCOPRef( kapp->dcopClient()->appId(), + cs->dcopObject()->objId(), + "KisColorSpaceIface" ); +} diff --git a/chalk/core/kis_imagepipe_brush.cc b/chalk/core/kis_imagepipe_brush.cc deleted file mode 100644 index 5c60b517..00000000 --- a/chalk/core/kis_imagepipe_brush.cc +++ /dev/null @@ -1,456 +0,0 @@ -/* - * Copyright (c) 2004 Boudewijn Rempt - * Copyright (c) 2005 Bart Coppens - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "kis_global.h" -#include "kis_paint_device.h" -#include "kis_imagepipe_brush.h" -#include "kis_brush.h" -#include "kis_alpha_mask.h" -#include "kis_layer.h" -#include "kis_meta_registry.h" -#include "kis_colorspace_factory_registry.h" - - -KisPipeBrushParasite::KisPipeBrushParasite(const TQString& source) -{ - needsMovement = false; - TQRegExp basicSplitter(" ", true); - TQRegExp parasiteSplitter(":", true); - TQStringList parasites = TQStringList::split(basicSplitter, source); - for (uint i = 0; i < parasites.count(); i++) { - TQStringList splitted = TQStringList::split(parasiteSplitter, *parasites.at(i)); - if (splitted.count() != 2) { - kdWarning(41001) << "Wrong count for this parasite key/value:" << *parasites.at(i) << endl; - continue; - } - TQString index = *splitted.at(0); - if (index == "dim") { - dim = (*splitted.at(1)).toInt(); - if (dim < 1 || dim > MaxDim) { - dim = 1; - } - } else if (index.startsWith("sel")) { - int selIndex = index.mid(strlen("sel")).toInt(); - if (selIndex >= 0 && selIndex < dim) { - TQString selectionMode = *splitted.at(1); - if (selectionMode == "incremental") - selection[selIndex] = Incremental; - else if (selectionMode == "angular") { - selection[selIndex] = Angular; - needsMovement = true; - } else if (selectionMode == "random") - selection[selIndex] = Random; - else if (selectionMode == "pressure") - selection[selIndex] = Pressure; - else if (selectionMode == "xtilt") - selection[selIndex] = TiltX; - else if (selectionMode == "ytilt") - selection[selIndex] = TiltY; - else - selection[selIndex] = Constant; - } else { - kdWarning(41001)<< "Sel: wrong index: " << selIndex << "(dim = " << dim << ")" << endl; - } - } else if (index.startsWith("rank")) { - int rankIndex = index.mid(strlen("rank")).toInt(); - if (rankIndex < 0 || rankIndex > dim) { - kdWarning(41001) << "Rankindex out of range: " << rankIndex << endl; - continue; - } - rank[rankIndex] = (*splitted.at(1)).toInt(); - } else if (index == "ncells") { - ncells = (*splitted.at(1)).toInt(); - if (ncells < 1 ) { - kdWarning(41001) << "ncells out of range: " << ncells << endl; - ncells = 1; - } - } - } - - for (int i = 0; i < dim; i++) { - index[i] = 0; - } - - setBrushesCount(); -} - -void KisPipeBrushParasite::setBrushesCount() { - // I assume ncells is correct. If it isn't, complain to the parasite header. - brushesCount[0] = ncells / rank[0]; - for (int i = 1; i < dim; i++) { - brushesCount[i] = brushesCount[i-1] / rank[i]; - } -} - -bool KisPipeBrushParasite::saveToDevice(TQIODevice* dev) const { - // write out something like - // ncells: dim: rank0: sel0: <...> - - TQTextStream stream(dev); - /// FIXME things like step, placement and so are not added (nor loaded, as a matter of fact) - stream << ncells << " ncells:" << ncells << " dim:" << dim; - - for (int i = 0; i < dim; i++) { - stream << " rank" << i << ":" << rank[i] << " sel" << i << ":"; - switch (selection[i]) { - case Constant: stream << "constant"; break; - case Incremental: stream << "incremental"; break; - case Angular: stream << "angular"; break; - case Velocity: stream << "velocity"; break; - case Random: stream << "random"; break; - case Pressure: stream << "pressure"; break; - case TiltX: stream << "xtilt"; break; - case TiltY: stream << "ytilt"; break; - } - } - - return true; -} - -KisImagePipeBrush::KisImagePipeBrush(const TQString& filename) : super(filename) -{ - m_brushType = INVALID; - m_numOfBrushes = 0; - m_currentBrush = 0; -} - -KisImagePipeBrush::KisImagePipeBrush(const TQString& name, int w, int h, - TQValueVector< TQValueVector > devices, - TQValueVector modes) - : super("") -{ - Q_ASSERT(devices.count() == modes.count()); - Q_ASSERT(devices.count() > 0); - Q_ASSERT(devices.count() < 2); // XXX Multidimensionals not supported yet, change to MaxDim! - - setName(name); - - m_parasite.dim = devices.count(); - // XXX Change for multidim! : - m_parasite.ncells = devices.at(0).count(); - m_parasite.rank[0] = m_parasite.ncells; - m_parasite.selection[0] = modes.at(0); - // XXX needsmovement! - - m_parasite.setBrushesCount(); - - for (uint i = 0; i < devices.at(0).count(); i++) { - m_brushes.append(new KisBrush(devices.at(0).at(i), 0, 0, w, h)); - } - - setImage(m_brushes.at(0)->img()); - - m_brushType = PIPE_IMAGE; -} - -KisImagePipeBrush::~KisImagePipeBrush() -{ - m_brushes.setAutoDelete(true); - m_brushes.clear(); -} - -bool KisImagePipeBrush::load() -{ - TQFile file(filename()); - file.open(IO_ReadOnly); - m_data = file.readAll(); - file.close(); - return init(); -} - -bool KisImagePipeBrush::init() -{ - // XXX: this doesn't correctly load the image pipe brushes yet. - - // XXX: This stuff is in utf-8, too. - // The first line contains the name -- this means we look until we arrive at the first newline - TQValueVector line1; - - TQ_UINT32 i = 0; - - while (m_data[i] != '\n' && i < m_data.size()) { - line1.append(m_data[i]); - i++; - } - setName(i18n(TQString::fromUtf8(&line1[0], i).ascii())); - - i++; // Skip past the first newline - - // The second line contains the number of brushes, separated by a space from the parasite - - // XXX: This stuff is in utf-8, too. - TQValueVector line2; - while (m_data[i] != '\n' && i < m_data.size()) { - line2.append(m_data[i]); - i++; - } - - TQString paramline = TQString::fromUtf8((&line2[0]), line2.size()); - TQ_UINT32 m_numOfBrushes = paramline.left(paramline.find(' ')).toUInt(); - m_parasite = paramline.mid(paramline.find(' ') + 1); - i++; // Skip past the second newline - - TQ_UINT32 numOfBrushes = 0; - while (numOfBrushes < m_numOfBrushes && i < m_data.size()){ - KisBrush * brush = new KisBrush(name() + "_" + numOfBrushes, - m_data, - i); - TQ_CHECK_PTR(brush); - - m_brushes.append(brush); - - numOfBrushes++; - } - - if (!m_brushes.isEmpty()) { - setValid(true); - if (m_brushes.at( 0 )->brushType() == MASK) { - m_brushType = PIPE_MASK; - } - else { - m_brushType = PIPE_IMAGE; - } - setSpacing(m_brushes.at(m_brushes.count() - 1)->spacing()); - setWidth(m_brushes.at(0)->width()); - setHeight(m_brushes.at(0)->height()); - } - - m_data.resize(0); - return true; -} - -bool KisImagePipeBrush::save() -{ - TQFile file(filename()); - file.open(IO_WriteOnly | IO_Truncate); - bool ok = saveToDevice(TQT_TQIODEVICE(&file)); - file.close(); - return ok; -} - -bool KisImagePipeBrush::saveToDevice(TQIODevice* dev) const -{ - TQCString utf8Name = name().utf8(); // Names in v2 brushes are in UTF-8 - char const* name = utf8Name.data(); - int len = tqstrlen(name); - - if (parasite().dim != 1) { - kdWarning(41001) << "Save to file for pipe brushes with dim != not yet supported!" << endl; - return false; - } - - // Save this pipe brush: first the header, and then all individual brushes consecutively - // (this needs some care for when we have > 1 dimension), FIXME - - // Gimp Pipe Brush header format: Name\n \n - - // The name\n - if (dev->writeBlock(name, len) == -1) - return false; - - if (dev->putch('\n') == -1) - return false; - - // Write the parasite (also writes number of brushes) - if (!m_parasite.saveToDevice(dev)) - return false; - - if (dev->putch('\n') == -1) - return false; - - // - for (uint i = 0; i < m_brushes.count(); i++) - if (!m_brushes.at(i)->saveToDevice(dev)) - return false; - - return true; -} - -TQImage KisImagePipeBrush::img() -{ - if (m_brushes.isEmpty()) { - return 0; - } - else { - return m_brushes.at(0)->img(); - } -} - -KisAlphaMaskSP KisImagePipeBrush::mask(const KisPaintInformation& info, double subPixelX, double subPixelY) const -{ - if (m_brushes.isEmpty()) return 0; - selectNextBrush(info); - return m_brushes.at(m_currentBrush)->mask(info, subPixelX, subPixelY); -} - -KisPaintDeviceSP KisImagePipeBrush::image(KisColorSpace * colorSpace, const KisPaintInformation& info, double subPixelX, double subPixelY) const -{ - if (m_brushes.isEmpty()) return 0; - selectNextBrush(info); - return m_brushes.at(m_currentBrush)->image(colorSpace, info, subPixelX, subPixelY); -} - -void KisImagePipeBrush::setParasiteString(const TQString& parasite) -{ - m_parasiteString = parasite; - m_parasite = KisPipeBrushParasite(parasite); -} - - -enumBrushType KisImagePipeBrush::brushType() const -{ - if (m_brushType == PIPE_IMAGE && useColorAsMask()) { - return PIPE_MASK; - } - else { - return m_brushType; - } -} - -bool KisImagePipeBrush::useColorAsMask() const -{ - if (m_brushes.count() > 0) { - return m_brushes.at(0)->useColorAsMask(); - } - else { - return false; - } -} - -void KisImagePipeBrush::setUseColorAsMask(bool useColorAsMask) -{ - for (uint i = 0; i < m_brushes.count(); i++) { - m_brushes.at(i)->setUseColorAsMask(useColorAsMask); - } -} - -bool KisImagePipeBrush::hasColor() const -{ - if (m_brushes.count() > 0) { - return m_brushes.at(0)->hasColor(); - } - else { - return false; - } -} - -KisBoundary KisImagePipeBrush::boundary() { - Q_ASSERT(!m_brushes.isEmpty()); - return m_brushes.at(0)->boundary(); -} - -void KisImagePipeBrush::selectNextBrush(const KisPaintInformation& info) const { - m_currentBrush = 0; - double angle; - for (int i = 0; i < m_parasite.dim; i++) { - int index = m_parasite.index[i]; - switch (m_parasite.selection[i]) { - case KisPipeBrushParasite::Constant: break; - case KisPipeBrushParasite::Incremental: - index = (index + 1) % m_parasite.rank[i]; break; - case KisPipeBrushParasite::Random: - index = int(float(m_parasite.rank[i])*TDEApplication::random() / RAND_MAX); break; - case KisPipeBrushParasite::Pressure: - index = static_cast(info.pressure * (m_parasite.rank[i] - 1) + 0.5); break; - case KisPipeBrushParasite::Angular: - // + M_PI_2 to be compatible with the gimp - angle = atan2(info.movement.y(), info.movement.x()) + M_PI_2; - // We need to be in the [0..2*Pi[ interval so that we can more nicely select it - if (angle < 0) - angle += 2.0 * M_PI; - else if (angle > 2.0 * M_PI) - angle -= 2.0 * M_PI; - index = static_cast(angle / (2.0 * M_PI) * m_parasite.rank[i]); - break; - default: - kdWarning(41001) << "This parasite selectionMode has not been implemented. Reselecting" - << " to Incremental" << endl; - m_parasite.selection[i] = KisPipeBrushParasite::Incremental; - index = 0; - } - m_parasite.index[i] = index; - m_currentBrush += m_parasite.brushesCount[i] * index; - } -} - -bool KisImagePipeBrush::canPaintFor(const KisPaintInformation& info) { - if (info.movement.isNull() && m_parasite.needsMovement) - return false; - return true; -} - -void KisImagePipeBrush::makeMaskImage() { - for (uint i = 0; i < m_brushes.count(); i++) - m_brushes.at(i)->makeMaskImage(); - - setBrushType(PIPE_MASK); - setUseColorAsMask(false); -} - -KisImagePipeBrush* KisImagePipeBrush::clone() const { - // The obvious way of cloning each brush in this one doesn't work for some reason... - - // XXX Multidimensionals not supported yet, change together with the constructor... - TQValueVector< TQValueVector > devices; - TQValueVector modes; - - devices.push_back(TQValueVector()); - modes.push_back(m_parasite.selection[0]); - - for (uint i = 0; i < m_brushes.count(); i++) { - KisPaintDevice* pd = new KisPaintDevice( - KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""), "clone pd" ); - pd->convertFromTQImage(m_brushes.at(i)->img(), ""); - devices.at(0).append(pd); - } - - KisImagePipeBrush* c = new KisImagePipeBrush(name(), width(), height(), devices, modes); - // XXX clean up devices - - return c; -} - -#include "kis_imagepipe_brush.moc" - diff --git a/chalk/core/kis_imagepipe_brush.cpp b/chalk/core/kis_imagepipe_brush.cpp new file mode 100644 index 00000000..5c60b517 --- /dev/null +++ b/chalk/core/kis_imagepipe_brush.cpp @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt + * Copyright (c) 2005 Bart Coppens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kis_global.h" +#include "kis_paint_device.h" +#include "kis_imagepipe_brush.h" +#include "kis_brush.h" +#include "kis_alpha_mask.h" +#include "kis_layer.h" +#include "kis_meta_registry.h" +#include "kis_colorspace_factory_registry.h" + + +KisPipeBrushParasite::KisPipeBrushParasite(const TQString& source) +{ + needsMovement = false; + TQRegExp basicSplitter(" ", true); + TQRegExp parasiteSplitter(":", true); + TQStringList parasites = TQStringList::split(basicSplitter, source); + for (uint i = 0; i < parasites.count(); i++) { + TQStringList splitted = TQStringList::split(parasiteSplitter, *parasites.at(i)); + if (splitted.count() != 2) { + kdWarning(41001) << "Wrong count for this parasite key/value:" << *parasites.at(i) << endl; + continue; + } + TQString index = *splitted.at(0); + if (index == "dim") { + dim = (*splitted.at(1)).toInt(); + if (dim < 1 || dim > MaxDim) { + dim = 1; + } + } else if (index.startsWith("sel")) { + int selIndex = index.mid(strlen("sel")).toInt(); + if (selIndex >= 0 && selIndex < dim) { + TQString selectionMode = *splitted.at(1); + if (selectionMode == "incremental") + selection[selIndex] = Incremental; + else if (selectionMode == "angular") { + selection[selIndex] = Angular; + needsMovement = true; + } else if (selectionMode == "random") + selection[selIndex] = Random; + else if (selectionMode == "pressure") + selection[selIndex] = Pressure; + else if (selectionMode == "xtilt") + selection[selIndex] = TiltX; + else if (selectionMode == "ytilt") + selection[selIndex] = TiltY; + else + selection[selIndex] = Constant; + } else { + kdWarning(41001)<< "Sel: wrong index: " << selIndex << "(dim = " << dim << ")" << endl; + } + } else if (index.startsWith("rank")) { + int rankIndex = index.mid(strlen("rank")).toInt(); + if (rankIndex < 0 || rankIndex > dim) { + kdWarning(41001) << "Rankindex out of range: " << rankIndex << endl; + continue; + } + rank[rankIndex] = (*splitted.at(1)).toInt(); + } else if (index == "ncells") { + ncells = (*splitted.at(1)).toInt(); + if (ncells < 1 ) { + kdWarning(41001) << "ncells out of range: " << ncells << endl; + ncells = 1; + } + } + } + + for (int i = 0; i < dim; i++) { + index[i] = 0; + } + + setBrushesCount(); +} + +void KisPipeBrushParasite::setBrushesCount() { + // I assume ncells is correct. If it isn't, complain to the parasite header. + brushesCount[0] = ncells / rank[0]; + for (int i = 1; i < dim; i++) { + brushesCount[i] = brushesCount[i-1] / rank[i]; + } +} + +bool KisPipeBrushParasite::saveToDevice(TQIODevice* dev) const { + // write out something like + // ncells: dim: rank0: sel0: <...> + + TQTextStream stream(dev); + /// FIXME things like step, placement and so are not added (nor loaded, as a matter of fact) + stream << ncells << " ncells:" << ncells << " dim:" << dim; + + for (int i = 0; i < dim; i++) { + stream << " rank" << i << ":" << rank[i] << " sel" << i << ":"; + switch (selection[i]) { + case Constant: stream << "constant"; break; + case Incremental: stream << "incremental"; break; + case Angular: stream << "angular"; break; + case Velocity: stream << "velocity"; break; + case Random: stream << "random"; break; + case Pressure: stream << "pressure"; break; + case TiltX: stream << "xtilt"; break; + case TiltY: stream << "ytilt"; break; + } + } + + return true; +} + +KisImagePipeBrush::KisImagePipeBrush(const TQString& filename) : super(filename) +{ + m_brushType = INVALID; + m_numOfBrushes = 0; + m_currentBrush = 0; +} + +KisImagePipeBrush::KisImagePipeBrush(const TQString& name, int w, int h, + TQValueVector< TQValueVector > devices, + TQValueVector modes) + : super("") +{ + Q_ASSERT(devices.count() == modes.count()); + Q_ASSERT(devices.count() > 0); + Q_ASSERT(devices.count() < 2); // XXX Multidimensionals not supported yet, change to MaxDim! + + setName(name); + + m_parasite.dim = devices.count(); + // XXX Change for multidim! : + m_parasite.ncells = devices.at(0).count(); + m_parasite.rank[0] = m_parasite.ncells; + m_parasite.selection[0] = modes.at(0); + // XXX needsmovement! + + m_parasite.setBrushesCount(); + + for (uint i = 0; i < devices.at(0).count(); i++) { + m_brushes.append(new KisBrush(devices.at(0).at(i), 0, 0, w, h)); + } + + setImage(m_brushes.at(0)->img()); + + m_brushType = PIPE_IMAGE; +} + +KisImagePipeBrush::~KisImagePipeBrush() +{ + m_brushes.setAutoDelete(true); + m_brushes.clear(); +} + +bool KisImagePipeBrush::load() +{ + TQFile file(filename()); + file.open(IO_ReadOnly); + m_data = file.readAll(); + file.close(); + return init(); +} + +bool KisImagePipeBrush::init() +{ + // XXX: this doesn't correctly load the image pipe brushes yet. + + // XXX: This stuff is in utf-8, too. + // The first line contains the name -- this means we look until we arrive at the first newline + TQValueVector line1; + + TQ_UINT32 i = 0; + + while (m_data[i] != '\n' && i < m_data.size()) { + line1.append(m_data[i]); + i++; + } + setName(i18n(TQString::fromUtf8(&line1[0], i).ascii())); + + i++; // Skip past the first newline + + // The second line contains the number of brushes, separated by a space from the parasite + + // XXX: This stuff is in utf-8, too. + TQValueVector line2; + while (m_data[i] != '\n' && i < m_data.size()) { + line2.append(m_data[i]); + i++; + } + + TQString paramline = TQString::fromUtf8((&line2[0]), line2.size()); + TQ_UINT32 m_numOfBrushes = paramline.left(paramline.find(' ')).toUInt(); + m_parasite = paramline.mid(paramline.find(' ') + 1); + i++; // Skip past the second newline + + TQ_UINT32 numOfBrushes = 0; + while (numOfBrushes < m_numOfBrushes && i < m_data.size()){ + KisBrush * brush = new KisBrush(name() + "_" + numOfBrushes, + m_data, + i); + TQ_CHECK_PTR(brush); + + m_brushes.append(brush); + + numOfBrushes++; + } + + if (!m_brushes.isEmpty()) { + setValid(true); + if (m_brushes.at( 0 )->brushType() == MASK) { + m_brushType = PIPE_MASK; + } + else { + m_brushType = PIPE_IMAGE; + } + setSpacing(m_brushes.at(m_brushes.count() - 1)->spacing()); + setWidth(m_brushes.at(0)->width()); + setHeight(m_brushes.at(0)->height()); + } + + m_data.resize(0); + return true; +} + +bool KisImagePipeBrush::save() +{ + TQFile file(filename()); + file.open(IO_WriteOnly | IO_Truncate); + bool ok = saveToDevice(TQT_TQIODEVICE(&file)); + file.close(); + return ok; +} + +bool KisImagePipeBrush::saveToDevice(TQIODevice* dev) const +{ + TQCString utf8Name = name().utf8(); // Names in v2 brushes are in UTF-8 + char const* name = utf8Name.data(); + int len = tqstrlen(name); + + if (parasite().dim != 1) { + kdWarning(41001) << "Save to file for pipe brushes with dim != not yet supported!" << endl; + return false; + } + + // Save this pipe brush: first the header, and then all individual brushes consecutively + // (this needs some care for when we have > 1 dimension), FIXME + + // Gimp Pipe Brush header format: Name\n \n + + // The name\n + if (dev->writeBlock(name, len) == -1) + return false; + + if (dev->putch('\n') == -1) + return false; + + // Write the parasite (also writes number of brushes) + if (!m_parasite.saveToDevice(dev)) + return false; + + if (dev->putch('\n') == -1) + return false; + + // + for (uint i = 0; i < m_brushes.count(); i++) + if (!m_brushes.at(i)->saveToDevice(dev)) + return false; + + return true; +} + +TQImage KisImagePipeBrush::img() +{ + if (m_brushes.isEmpty()) { + return 0; + } + else { + return m_brushes.at(0)->img(); + } +} + +KisAlphaMaskSP KisImagePipeBrush::mask(const KisPaintInformation& info, double subPixelX, double subPixelY) const +{ + if (m_brushes.isEmpty()) return 0; + selectNextBrush(info); + return m_brushes.at(m_currentBrush)->mask(info, subPixelX, subPixelY); +} + +KisPaintDeviceSP KisImagePipeBrush::image(KisColorSpace * colorSpace, const KisPaintInformation& info, double subPixelX, double subPixelY) const +{ + if (m_brushes.isEmpty()) return 0; + selectNextBrush(info); + return m_brushes.at(m_currentBrush)->image(colorSpace, info, subPixelX, subPixelY); +} + +void KisImagePipeBrush::setParasiteString(const TQString& parasite) +{ + m_parasiteString = parasite; + m_parasite = KisPipeBrushParasite(parasite); +} + + +enumBrushType KisImagePipeBrush::brushType() const +{ + if (m_brushType == PIPE_IMAGE && useColorAsMask()) { + return PIPE_MASK; + } + else { + return m_brushType; + } +} + +bool KisImagePipeBrush::useColorAsMask() const +{ + if (m_brushes.count() > 0) { + return m_brushes.at(0)->useColorAsMask(); + } + else { + return false; + } +} + +void KisImagePipeBrush::setUseColorAsMask(bool useColorAsMask) +{ + for (uint i = 0; i < m_brushes.count(); i++) { + m_brushes.at(i)->setUseColorAsMask(useColorAsMask); + } +} + +bool KisImagePipeBrush::hasColor() const +{ + if (m_brushes.count() > 0) { + return m_brushes.at(0)->hasColor(); + } + else { + return false; + } +} + +KisBoundary KisImagePipeBrush::boundary() { + Q_ASSERT(!m_brushes.isEmpty()); + return m_brushes.at(0)->boundary(); +} + +void KisImagePipeBrush::selectNextBrush(const KisPaintInformation& info) const { + m_currentBrush = 0; + double angle; + for (int i = 0; i < m_parasite.dim; i++) { + int index = m_parasite.index[i]; + switch (m_parasite.selection[i]) { + case KisPipeBrushParasite::Constant: break; + case KisPipeBrushParasite::Incremental: + index = (index + 1) % m_parasite.rank[i]; break; + case KisPipeBrushParasite::Random: + index = int(float(m_parasite.rank[i])*TDEApplication::random() / RAND_MAX); break; + case KisPipeBrushParasite::Pressure: + index = static_cast(info.pressure * (m_parasite.rank[i] - 1) + 0.5); break; + case KisPipeBrushParasite::Angular: + // + M_PI_2 to be compatible with the gimp + angle = atan2(info.movement.y(), info.movement.x()) + M_PI_2; + // We need to be in the [0..2*Pi[ interval so that we can more nicely select it + if (angle < 0) + angle += 2.0 * M_PI; + else if (angle > 2.0 * M_PI) + angle -= 2.0 * M_PI; + index = static_cast(angle / (2.0 * M_PI) * m_parasite.rank[i]); + break; + default: + kdWarning(41001) << "This parasite selectionMode has not been implemented. Reselecting" + << " to Incremental" << endl; + m_parasite.selection[i] = KisPipeBrushParasite::Incremental; + index = 0; + } + m_parasite.index[i] = index; + m_currentBrush += m_parasite.brushesCount[i] * index; + } +} + +bool KisImagePipeBrush::canPaintFor(const KisPaintInformation& info) { + if (info.movement.isNull() && m_parasite.needsMovement) + return false; + return true; +} + +void KisImagePipeBrush::makeMaskImage() { + for (uint i = 0; i < m_brushes.count(); i++) + m_brushes.at(i)->makeMaskImage(); + + setBrushType(PIPE_MASK); + setUseColorAsMask(false); +} + +KisImagePipeBrush* KisImagePipeBrush::clone() const { + // The obvious way of cloning each brush in this one doesn't work for some reason... + + // XXX Multidimensionals not supported yet, change together with the constructor... + TQValueVector< TQValueVector > devices; + TQValueVector modes; + + devices.push_back(TQValueVector()); + modes.push_back(m_parasite.selection[0]); + + for (uint i = 0; i < m_brushes.count(); i++) { + KisPaintDevice* pd = new KisPaintDevice( + KisMetaRegistry::instance()->csRegistry()->getColorSpace(KisID("RGBA",""),""), "clone pd" ); + pd->convertFromTQImage(m_brushes.at(i)->img(), ""); + devices.at(0).append(pd); + } + + KisImagePipeBrush* c = new KisImagePipeBrush(name(), width(), height(), devices, modes); + // XXX clean up devices + + return c; +} + +#include "kis_imagepipe_brush.moc" + diff --git a/chalk/core/kis_iterator.cc b/chalk/core/kis_iterator.cc deleted file mode 100644 index 0e0c2d81..00000000 --- a/chalk/core/kis_iterator.cc +++ /dev/null @@ -1,142 +0,0 @@ -/* This file is part of the KDE project - * Copyright (c) 2004 Casper Boemann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - - -#include - -#include - -#include "kis_iterator.h" -#include "kis_datamanager.h" -#include "kis_tilediterator.h" - -KisRectIterator::KisRectIterator ( KisDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, bool writable) -{ - m_iter = new KisTiledRectIterator(dm, x, y, w, h, writable); -} -KisRectIterator::KisRectIterator(const KisRectIterator& rhs) -{ - m_iter = rhs.m_iter; -} - -KisRectIterator& KisRectIterator::operator=(const KisRectIterator& rhs) -{ - m_iter = rhs.m_iter; - return *this; -} - -KisRectIterator::~KisRectIterator() -{ -} - -TQ_UINT8 * KisRectIterator::rawData() const { return m_iter->rawData();} - -const TQ_UINT8 * KisRectIterator::oldRawData() const { return m_iter->oldRawData();} - -TQ_INT32 KisRectIterator::nConseqPixels() const { return m_iter->nConseqPixels(); } - -KisRectIterator & KisRectIterator::operator+=(int n) { m_iter->operator+=(n); return *this; } - -KisRectIterator & KisRectIterator::operator++() { m_iter->operator++(); return *this; } - -bool KisRectIterator::isDone() const { return m_iter->isDone(); } - -TQ_INT32 KisRectIterator::x() const { return m_iter->x(); } -TQ_INT32 KisRectIterator::y() const { return m_iter->y(); } - -//--------------------------------------------------------------------------------------- - -KisHLineIterator::KisHLineIterator ( KisDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable) -{ - m_iter = new KisTiledHLineIterator(dm, x, y, w, writable); -} - -KisHLineIterator::KisHLineIterator(const KisHLineIterator& rhs) -{ - m_iter = rhs.m_iter; -} - -KisHLineIterator& KisHLineIterator::operator=(const KisHLineIterator& rhs) -{ - - m_iter=rhs.m_iter; - return *this; -} - -KisHLineIterator::~KisHLineIterator() -{ -} - -TQ_UINT8 *KisHLineIterator::rawData() const -{ - return m_iter->rawData(); -} - -const TQ_UINT8 *KisHLineIterator::oldRawData() const { return m_iter->oldRawData();} - -KisHLineIterator & KisHLineIterator::operator++() { m_iter->operator++(); return *this; } - -TQ_INT32 KisHLineIterator::nConseqHPixels() const { return m_iter->nConseqHPixels(); } - -KisHLineIterator & KisHLineIterator::operator+=(int n) { m_iter->operator+=(n); return *this; } - -KisHLineIterator & KisHLineIterator::operator--() { m_iter->operator--(); return *this; } - -bool KisHLineIterator::isDone() const { return m_iter->isDone(); } - -TQ_INT32 KisHLineIterator::x() const { return m_iter->x(); } - -TQ_INT32 KisHLineIterator::y() const { return m_iter->y(); } - -void KisHLineIterator::nextRow() { m_iter->nextRow(); } - -//--------------------------------------------------------------------------------------- - -KisVLineIterator::KisVLineIterator ( KisDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable) -{ - m_iter = new KisTiledVLineIterator(dm, x, y, h, writable); -} - -KisVLineIterator::KisVLineIterator(const KisVLineIterator& rhs) -{ - m_iter = rhs.m_iter; -} - -KisVLineIterator& KisVLineIterator::operator=(const KisVLineIterator& rhs) -{ - m_iter = rhs.m_iter; - return *this; -} - -KisVLineIterator::~KisVLineIterator() -{ -} - -TQ_UINT8 *KisVLineIterator::rawData() const { return m_iter->rawData();} - -const TQ_UINT8 * KisVLineIterator::oldRawData() const { return m_iter->oldRawData();} - -KisVLineIterator & KisVLineIterator::operator++() { m_iter->operator++(); return *this; } - -bool KisVLineIterator::isDone() const { return m_iter->isDone(); } - -TQ_INT32 KisVLineIterator::x() const { return m_iter->x(); } - -TQ_INT32 KisVLineIterator::y() const { return m_iter->y(); } - -void KisVLineIterator::nextCol() { return m_iter->nextCol(); } diff --git a/chalk/core/kis_iterator.cpp b/chalk/core/kis_iterator.cpp new file mode 100644 index 00000000..0e0c2d81 --- /dev/null +++ b/chalk/core/kis_iterator.cpp @@ -0,0 +1,142 @@ +/* This file is part of the KDE project + * Copyright (c) 2004 Casper Boemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + + +#include + +#include + +#include "kis_iterator.h" +#include "kis_datamanager.h" +#include "kis_tilediterator.h" + +KisRectIterator::KisRectIterator ( KisDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, bool writable) +{ + m_iter = new KisTiledRectIterator(dm, x, y, w, h, writable); +} +KisRectIterator::KisRectIterator(const KisRectIterator& rhs) +{ + m_iter = rhs.m_iter; +} + +KisRectIterator& KisRectIterator::operator=(const KisRectIterator& rhs) +{ + m_iter = rhs.m_iter; + return *this; +} + +KisRectIterator::~KisRectIterator() +{ +} + +TQ_UINT8 * KisRectIterator::rawData() const { return m_iter->rawData();} + +const TQ_UINT8 * KisRectIterator::oldRawData() const { return m_iter->oldRawData();} + +TQ_INT32 KisRectIterator::nConseqPixels() const { return m_iter->nConseqPixels(); } + +KisRectIterator & KisRectIterator::operator+=(int n) { m_iter->operator+=(n); return *this; } + +KisRectIterator & KisRectIterator::operator++() { m_iter->operator++(); return *this; } + +bool KisRectIterator::isDone() const { return m_iter->isDone(); } + +TQ_INT32 KisRectIterator::x() const { return m_iter->x(); } +TQ_INT32 KisRectIterator::y() const { return m_iter->y(); } + +//--------------------------------------------------------------------------------------- + +KisHLineIterator::KisHLineIterator ( KisDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable) +{ + m_iter = new KisTiledHLineIterator(dm, x, y, w, writable); +} + +KisHLineIterator::KisHLineIterator(const KisHLineIterator& rhs) +{ + m_iter = rhs.m_iter; +} + +KisHLineIterator& KisHLineIterator::operator=(const KisHLineIterator& rhs) +{ + + m_iter=rhs.m_iter; + return *this; +} + +KisHLineIterator::~KisHLineIterator() +{ +} + +TQ_UINT8 *KisHLineIterator::rawData() const +{ + return m_iter->rawData(); +} + +const TQ_UINT8 *KisHLineIterator::oldRawData() const { return m_iter->oldRawData();} + +KisHLineIterator & KisHLineIterator::operator++() { m_iter->operator++(); return *this; } + +TQ_INT32 KisHLineIterator::nConseqHPixels() const { return m_iter->nConseqHPixels(); } + +KisHLineIterator & KisHLineIterator::operator+=(int n) { m_iter->operator+=(n); return *this; } + +KisHLineIterator & KisHLineIterator::operator--() { m_iter->operator--(); return *this; } + +bool KisHLineIterator::isDone() const { return m_iter->isDone(); } + +TQ_INT32 KisHLineIterator::x() const { return m_iter->x(); } + +TQ_INT32 KisHLineIterator::y() const { return m_iter->y(); } + +void KisHLineIterator::nextRow() { m_iter->nextRow(); } + +//--------------------------------------------------------------------------------------- + +KisVLineIterator::KisVLineIterator ( KisDataManager *dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable) +{ + m_iter = new KisTiledVLineIterator(dm, x, y, h, writable); +} + +KisVLineIterator::KisVLineIterator(const KisVLineIterator& rhs) +{ + m_iter = rhs.m_iter; +} + +KisVLineIterator& KisVLineIterator::operator=(const KisVLineIterator& rhs) +{ + m_iter = rhs.m_iter; + return *this; +} + +KisVLineIterator::~KisVLineIterator() +{ +} + +TQ_UINT8 *KisVLineIterator::rawData() const { return m_iter->rawData();} + +const TQ_UINT8 * KisVLineIterator::oldRawData() const { return m_iter->oldRawData();} + +KisVLineIterator & KisVLineIterator::operator++() { m_iter->operator++(); return *this; } + +bool KisVLineIterator::isDone() const { return m_iter->isDone(); } + +TQ_INT32 KisVLineIterator::x() const { return m_iter->x(); } + +TQ_INT32 KisVLineIterator::y() const { return m_iter->y(); } + +void KisVLineIterator::nextCol() { return m_iter->nextCol(); } diff --git a/chalk/core/kis_iterators_pixel.cc b/chalk/core/kis_iterators_pixel.cc deleted file mode 100644 index 741a20b0..00000000 --- a/chalk/core/kis_iterators_pixel.cc +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is part of the Chalk project - * - * Copyright (c) 2004 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "kis_iterators_pixel.h" -#include "kis_global.h" -#include "kis_paint_device.h" - -KisHLineIteratorPixel::KisHLineIteratorPixel( KisPaintDevice *ndevice, KisDataManager *dm, KisDataManager *sel_dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 offsetx, TQ_INT32 offsety, bool writable) : - KisHLineIterator(dm, x - offsetx, y - offsety, w, writable), - KisIteratorPixelTrait ( ndevice, this ), - m_offsetx(offsetx), m_offsety(offsety) -{ - if(sel_dm) { - KisHLineIterator * i = new KisHLineIterator(sel_dm, x - offsetx, y - offsety, w, false); - TQ_CHECK_PTR(i); - KisIteratorPixelTrait ::setSelectionIterator(i); - } -} - -KisVLineIteratorPixel::KisVLineIteratorPixel( KisPaintDevice *ndevice, KisDataManager *dm, KisDataManager *sel_dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, TQ_INT32 offsetx, TQ_INT32 offsety, bool writable) : - KisVLineIterator(dm, x - offsetx, y - offsety, h, writable), - KisIteratorPixelTrait ( ndevice, this ), - m_offsetx(offsetx), m_offsety(offsety) -{ - if(sel_dm) { - KisVLineIterator * i = new KisVLineIterator(sel_dm, x - offsetx, y - offsety, h, false); - TQ_CHECK_PTR(i); - KisIteratorPixelTrait ::setSelectionIterator(i); - } -} - -KisRectIteratorPixel::KisRectIteratorPixel( KisPaintDevice *ndevice, KisDataManager *dm, KisDataManager *sel_dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, TQ_INT32 offsetx, TQ_INT32 offsety, bool writable) : - KisRectIterator(dm, x - offsetx, y - offsety, w, h, writable), - KisIteratorPixelTrait ( ndevice, this ), - m_offsetx(offsetx), m_offsety(offsety) -{ - if(sel_dm) { - KisRectIterator * i = new KisRectIterator(sel_dm, x - offsetx, y - offsety, w, h, false); - TQ_CHECK_PTR(i); - KisIteratorPixelTrait ::setSelectionIterator(i); - } -} diff --git a/chalk/core/kis_iterators_pixel.cpp b/chalk/core/kis_iterators_pixel.cpp new file mode 100644 index 00000000..741a20b0 --- /dev/null +++ b/chalk/core/kis_iterators_pixel.cpp @@ -0,0 +1,59 @@ +/* + * This file is part of the Chalk project + * + * Copyright (c) 2004 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_iterators_pixel.h" +#include "kis_global.h" +#include "kis_paint_device.h" + +KisHLineIteratorPixel::KisHLineIteratorPixel( KisPaintDevice *ndevice, KisDataManager *dm, KisDataManager *sel_dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 offsetx, TQ_INT32 offsety, bool writable) : + KisHLineIterator(dm, x - offsetx, y - offsety, w, writable), + KisIteratorPixelTrait ( ndevice, this ), + m_offsetx(offsetx), m_offsety(offsety) +{ + if(sel_dm) { + KisHLineIterator * i = new KisHLineIterator(sel_dm, x - offsetx, y - offsety, w, false); + TQ_CHECK_PTR(i); + KisIteratorPixelTrait ::setSelectionIterator(i); + } +} + +KisVLineIteratorPixel::KisVLineIteratorPixel( KisPaintDevice *ndevice, KisDataManager *dm, KisDataManager *sel_dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, TQ_INT32 offsetx, TQ_INT32 offsety, bool writable) : + KisVLineIterator(dm, x - offsetx, y - offsety, h, writable), + KisIteratorPixelTrait ( ndevice, this ), + m_offsetx(offsetx), m_offsety(offsety) +{ + if(sel_dm) { + KisVLineIterator * i = new KisVLineIterator(sel_dm, x - offsetx, y - offsety, h, false); + TQ_CHECK_PTR(i); + KisIteratorPixelTrait ::setSelectionIterator(i); + } +} + +KisRectIteratorPixel::KisRectIteratorPixel( KisPaintDevice *ndevice, KisDataManager *dm, KisDataManager *sel_dm, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, TQ_INT32 offsetx, TQ_INT32 offsety, bool writable) : + KisRectIterator(dm, x - offsetx, y - offsety, w, h, writable), + KisIteratorPixelTrait ( ndevice, this ), + m_offsetx(offsetx), m_offsety(offsety) +{ + if(sel_dm) { + KisRectIterator * i = new KisRectIterator(sel_dm, x - offsetx, y - offsety, w, h, false); + TQ_CHECK_PTR(i); + KisIteratorPixelTrait ::setSelectionIterator(i); + } +} diff --git a/chalk/core/kis_layer.cc b/chalk/core/kis_layer.cc deleted file mode 100644 index f43570f0..00000000 --- a/chalk/core/kis_layer.cc +++ /dev/null @@ -1,611 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * Copyright (c) 2005 Casper Boemann - * - * this program is free software; you can redistribute it and/or modify - * it under the terms of the gnu general public license as published by - * the free software foundation; either version 2 of the license, or - * (at your option) any later version. - * - * this program is distributed in the hope that it will be useful, - * but without any warranty; without even the implied warranty of - * merchantability or fitness for a particular purpose. see the - * gnu general public license for more details. - * - * you should have received a copy of the gnu general public license - * along with this program; if not, write to the free software - * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. - */ - -#include -#include - -#include "kis_debug_areas.h" -#include "kis_group_layer.h" -#include "kis_image.h" -#include "kis_layer.h" -#include "kis_painter.h" -#include "kis_undo_adapter.h" - -namespace { - - class KisLayerCommand : public KNamedCommand { - typedef KNamedCommand super; - - public: - KisLayerCommand(const TQString& name, KisLayerSP layer); - virtual ~KisLayerCommand() {} - - virtual void execute() = 0; - virtual void unexecute() = 0; - - protected: - void setUndo(bool undo); - - KisLayerSP m_layer; - }; - - KisLayerCommand::KisLayerCommand(const TQString& name, KisLayerSP layer) : - super(name), m_layer(layer) - { - } - - void KisLayerCommand::setUndo(bool undo) - { - if (m_layer->undoAdapter()) { - m_layer->undoAdapter()->setUndo(undo); - } - } - - class KisLayerLockedCommand : public KisLayerCommand { - typedef KisLayerCommand super; - - public: - KisLayerLockedCommand(KisLayerSP layer, bool newLocked); - - virtual void execute(); - virtual void unexecute(); - - private: - bool m_newLocked; - }; - - KisLayerLockedCommand::KisLayerLockedCommand(KisLayerSP layer, bool newLocked) : - super(i18n("Lock Layer"), layer) - { - m_newLocked = newLocked; - } - - void KisLayerLockedCommand::execute() - { - setUndo(false); - m_layer->setLocked(m_newLocked); - setUndo(true); - } - - void KisLayerLockedCommand::unexecute() - { - setUndo(false); - m_layer->setLocked(!m_newLocked); - setUndo(true); - } - - class KisLayerOpacityCommand : public KisLayerCommand { - typedef KisLayerCommand super; - - public: - KisLayerOpacityCommand(KisLayerSP layer, TQ_UINT8 oldOpacity, TQ_UINT8 newOpacity); - - virtual void execute(); - virtual void unexecute(); - - private: - TQ_UINT8 m_oldOpacity; - TQ_UINT8 m_newOpacity; - }; - - KisLayerOpacityCommand::KisLayerOpacityCommand(KisLayerSP layer, TQ_UINT8 oldOpacity, TQ_UINT8 newOpacity) : - super(i18n("Layer Opacity"), layer) - { - m_oldOpacity = oldOpacity; - m_newOpacity = newOpacity; - } - - void KisLayerOpacityCommand::execute() - { - setUndo(false); - m_layer->setOpacity(m_newOpacity); - setUndo(true); - } - - void KisLayerOpacityCommand::unexecute() - { - setUndo(false); - m_layer->setOpacity(m_oldOpacity); - setUndo(true); - } - - class KisLayerVisibilityCommand : public KisLayerCommand { - typedef KisLayerCommand super; - - public: - KisLayerVisibilityCommand(KisLayerSP layer, bool newVisibility); - - virtual void execute(); - virtual void unexecute(); - - private: - bool m_newVisibility; - }; - - KisLayerVisibilityCommand::KisLayerVisibilityCommand(KisLayerSP layer, bool newVisibility) : - super(i18n("Layer Visibility"), layer) - { - m_newVisibility = newVisibility; - } - - void KisLayerVisibilityCommand::execute() - { - setUndo(false); - m_layer->setVisible(m_newVisibility); - setUndo(true); - } - - void KisLayerVisibilityCommand::unexecute() - { - setUndo(false); - m_layer->setVisible(!m_newVisibility); - setUndo(true); - } - - class KisLayerCompositeOpCommand : public KisLayerCommand { - typedef KisLayerCommand super; - - public: - KisLayerCompositeOpCommand(KisLayerSP layer, const KisCompositeOp& oldCompositeOp, const KisCompositeOp& newCompositeOp); - - virtual void execute(); - virtual void unexecute(); - - private: - KisCompositeOp m_oldCompositeOp; - KisCompositeOp m_newCompositeOp; - }; - - KisLayerCompositeOpCommand::KisLayerCompositeOpCommand(KisLayerSP layer, const KisCompositeOp& oldCompositeOp, - const KisCompositeOp& newCompositeOp) : - super(i18n("Layer Composite Mode"), layer) - { - m_oldCompositeOp = oldCompositeOp; - m_newCompositeOp = newCompositeOp; - } - - void KisLayerCompositeOpCommand::execute() - { - setUndo(false); - m_layer->setCompositeOp(m_newCompositeOp); - setUndo(true); - } - - void KisLayerCompositeOpCommand::unexecute() - { - setUndo(false); - m_layer->setCompositeOp(m_oldCompositeOp); - setUndo(true); - } - - class KisLayerOffsetCommand : public KNamedCommand { - typedef KNamedCommand super; - - public: - KisLayerOffsetCommand(KisLayerSP layer, const TQPoint& oldpos, const TQPoint& newpos); - virtual ~KisLayerOffsetCommand(); - - virtual void execute(); - virtual void unexecute(); - - private: - void moveTo(const TQPoint& pos); - - private: - KisLayerSP m_layer; - TQRect m_updateRect; - TQPoint m_oldPos; - TQPoint m_newPos; - }; - - KisLayerOffsetCommand::KisLayerOffsetCommand(KisLayerSP layer, const TQPoint& oldpos, const TQPoint& newpos) : - super(i18n("Move Layer")) - { - m_layer = layer; - m_oldPos = oldpos; - m_newPos = newpos; - - TQRect currentBounds = m_layer->exactBounds(); - TQRect oldBounds = currentBounds; - oldBounds.moveBy(oldpos.x() - newpos.x(), oldpos.y() - newpos.y()); - - m_updateRect = currentBounds | oldBounds; - } - - KisLayerOffsetCommand::~KisLayerOffsetCommand() - { - } - - void KisLayerOffsetCommand::execute() - { - moveTo(m_newPos); - } - - void KisLayerOffsetCommand::unexecute() - { - moveTo(m_oldPos); - } - - void KisLayerOffsetCommand::moveTo(const TQPoint& pos) - { - if (m_layer->undoAdapter()) { - m_layer->undoAdapter()->setUndo(false); - } - - m_layer->setX(pos.x()); - m_layer->setY(pos.y()); - - m_layer->setDirty(m_updateRect); - - if (m_layer->undoAdapter()) { - m_layer->undoAdapter()->setUndo(true); - } - } -} - -static int getID() -{ - static int id = 1; - return id++; -} - - -KisLayer::KisLayer(KisImage *img, const TQString &name, TQ_UINT8 opacity) : - TQObject(0, name.latin1()), - TDEShared(), - m_id(getID()), - m_index(-1), - m_opacity(opacity), - m_locked(false), - m_visible(true), - m_temporary(false), - m_name(name), - m_parent(0), - m_image(img), - m_compositeOp(COMPOSITE_OVER) -{ -} - -KisLayer::KisLayer(const KisLayer& rhs) : - TQObject(), - TDEShared(rhs) -{ - if (this != &rhs) { - m_id = getID(); - m_index = -1; - m_opacity = rhs.m_opacity; - m_locked = rhs.m_locked; - m_visible = rhs.m_visible; - m_temporary = rhs.m_temporary; - m_dirtyRect = rhs.m_dirtyRect; - m_name = rhs.m_name; - m_image = rhs.m_image; - m_parent = 0; - m_compositeOp = rhs.m_compositeOp; - } -} - -KisLayer::~KisLayer() -{ -} - -void KisLayer::setClean(const TQRect & rect) -{ - if (m_dirtyRect.isValid() && rect.isValid()) { - - // XXX: We should only set the parts clean that were actually cleaned. However, extent and exactBounds conspire - // to make that very hard atm. - //if (rect.contains(m_dirtyRect)) m_dirtyRect = TQRect(); - m_dirtyRect = TQRect(); - } - -} - -bool KisLayer::dirty() -{ - return m_dirtyRect.isValid(); -} - - -bool KisLayer::dirty(const TQRect & rc) -{ - if (!m_dirtyRect.isValid() || !rc.isValid()) return false; - - return rc.intersects(m_dirtyRect); -} - -TQRect KisLayer::dirtyRect() const -{ - return m_dirtyRect; -} - -void KisLayer::setDirty(bool propagate) -{ - TQRect rc = extent(); - - if (rc.isValid()) m_dirtyRect = rc; - - // If we're dirty, our parent is dirty, if we've got a parent - if (propagate && m_parent && rc.isValid()) m_parent->setDirty(m_dirtyRect); - - if (m_image && rc.isValid()) { - m_image->notifyLayerUpdated(this, rc); - } -} - -void KisLayer::setDirty(const TQRect & rc, bool propagate) -{ - // If we're dirty, our parent is dirty, if we've got a parent - - if (rc.isValid()) - m_dirtyRect |= rc; - - if (propagate && m_parent && m_dirtyRect.isValid()) - m_parent->setDirty(m_dirtyRect); - - if (m_image && rc.isValid()) { - m_image->notifyLayerUpdated(this, rc); - } -} - -KisGroupLayerSP KisLayer::parent() const -{ - return m_parent; -} - -KisLayerSP KisLayer::prevSibling() const -{ - if (!parent()) - return 0; - return parent()->at(index() - 1); -} - -KisLayerSP KisLayer::nextSibling() const -{ - if (!parent()) - return 0; - return parent()->at(index() + 1); -} - -int KisLayer::index() const -{ - return m_index; -} - -void KisLayer::setIndex(int i) -{ - if (!parent()) - return; - parent()->setIndex(this, i); -} - -KisLayerSP KisLayer::findLayer(const TQString& n) const -{ - if (name() == n) - return const_cast(this); //HACK any less ugly way? findLayer() is conceptually const... - for (KisLayerSP layer = firstChild(); layer; layer = layer->nextSibling()) - if (KisLayerSP found = layer->findLayer(n)) - return found; - return 0; -} - -KisLayerSP KisLayer::findLayer(int i) const -{ - if (id() == i) - return const_cast(this); //HACK - for (KisLayerSP layer = firstChild(); layer; layer = layer->nextSibling()) - if (KisLayerSP found = layer->findLayer(i)) - return found; - return 0; -} - -int KisLayer::numLayers(int flags) const -{ - int num = 0; - if (matchesFlags(flags)) num++; - for (KisLayerSP layer = firstChild(); layer; layer = layer->nextSibling()) - num += layer->numLayers(flags); - return num; -} - -bool KisLayer::matchesFlags(int flags) const -{ - if ((flags & Visible) && !visible()) - return false; - if ((flags & Hidden) && visible()) - return false; - if ((flags & Locked) && !locked()) - return false; - if ((flags & Unlocked) && locked()) - return false; - return true; -} - -TQ_UINT8 KisLayer::opacity() const -{ - return m_opacity; -} - -void KisLayer::setOpacity(TQ_UINT8 val) -{ - if (m_opacity != val) - { - m_opacity = val; - setDirty(); - notifyPropertyChanged(); - } -} - -KNamedCommand *KisLayer::setOpacityCommand(TQ_UINT8 newOpacity) -{ - return new KisLayerOpacityCommand(this, opacity(), newOpacity); -} - -KNamedCommand *KisLayer::setOpacityCommand(TQ_UINT8 prevOpacity, TQ_UINT8 newOpacity) -{ - return new KisLayerOpacityCommand(this, prevOpacity, newOpacity); -} - -const bool KisLayer::visible() const -{ - return m_visible; -} - -void KisLayer::setVisible(bool v) -{ - if (m_visible != v) { - - m_visible = v; - notifyPropertyChanged(); - setDirty(); - - if (undoAdapter() && undoAdapter()->undo()) { - undoAdapter()->addCommand(setVisibleCommand(v)); - } - } -} - -KNamedCommand *KisLayer::setVisibleCommand(bool newVisibility) -{ - return new KisLayerVisibilityCommand(this, newVisibility); -} - -bool KisLayer::locked() const -{ - return m_locked; -} - -void KisLayer::setLocked(bool l) -{ - if (m_locked != l) { - m_locked = l; - notifyPropertyChanged(); - - if (undoAdapter() && undoAdapter()->undo()) { - undoAdapter()->addCommand(setLockedCommand(l)); - } - } -} - -bool KisLayer::temporary() const -{ - return m_temporary; -} - -void KisLayer::setTemporary(bool t) -{ - m_temporary = t; -} - -KNamedCommand *KisLayer::setLockedCommand(bool newLocked) -{ - return new KisLayerLockedCommand(this, newLocked); -} - -TQString KisLayer::name() const -{ - return m_name; -} - -void KisLayer::setName(const TQString& name) -{ - if (!name.isEmpty() && m_name != name) - { - m_name = name; - notifyPropertyChanged(); - } -} - -void KisLayer::setCompositeOp(const KisCompositeOp& compositeOp) -{ - if (m_compositeOp != compositeOp) - { - m_compositeOp = compositeOp; - notifyPropertyChanged(); - setDirty(); - - } -} - -KNamedCommand *KisLayer::setCompositeOpCommand(const KisCompositeOp& newCompositeOp) -{ - return new KisLayerCompositeOpCommand(this, compositeOp(), newCompositeOp); -} - -KNamedCommand *KisLayer::moveCommand(TQPoint oldPosition, TQPoint newPosition) -{ - return new KisLayerOffsetCommand(this, oldPosition, newPosition); -} - -KisUndoAdapter *KisLayer::undoAdapter() const -{ - if (m_image) { - return m_image->undoAdapter(); - } - return 0; -} - -void KisLayer::paintMaskInactiveLayers(TQImage &, TQ_INT32, TQ_INT32, TQ_INT32, TQ_INT32) -{ -} - -void KisLayer::paintSelection(TQImage &, TQ_INT32, TQ_INT32, TQ_INT32, TQ_INT32) -{ -} - -void KisLayer::paintSelection(TQImage &, const TQRect&, const TQSize&, const TQSize&) -{ -} - -TQImage KisLayer::createThumbnail(TQ_INT32, TQ_INT32) -{ - return 0; -} - -void KisLayer::notifyPropertyChanged() -{ - if(image() && !signalsBlocked()) - image()->notifyPropertyChanged(this); -} - -void KisLayerSupportsIndirectPainting::setTemporaryTarget(KisPaintDeviceSP t) { - m_temporaryTarget = t; -} - -void KisLayerSupportsIndirectPainting::setTemporaryCompositeOp(const KisCompositeOp& c) { - m_compositeOp = c; -} - -void KisLayerSupportsIndirectPainting::setTemporaryOpacity(TQ_UINT8 o) { - m_compositeOpacity = o; -} - -KisPaintDeviceSP KisLayerSupportsIndirectPainting::temporaryTarget() { - return m_temporaryTarget; -} - -KisCompositeOp KisLayerSupportsIndirectPainting::temporaryCompositeOp() const { - return m_compositeOp; -} - -TQ_UINT8 KisLayerSupportsIndirectPainting::temporaryOpacity() const { - return m_compositeOpacity; -} - -#include "kis_layer.moc" diff --git a/chalk/core/kis_layer.cpp b/chalk/core/kis_layer.cpp new file mode 100644 index 00000000..f43570f0 --- /dev/null +++ b/chalk/core/kis_layer.cpp @@ -0,0 +1,611 @@ +/* + * Copyright (c) 2002 Patrick Julien + * Copyright (c) 2005 Casper Boemann + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. + */ + +#include +#include + +#include "kis_debug_areas.h" +#include "kis_group_layer.h" +#include "kis_image.h" +#include "kis_layer.h" +#include "kis_painter.h" +#include "kis_undo_adapter.h" + +namespace { + + class KisLayerCommand : public KNamedCommand { + typedef KNamedCommand super; + + public: + KisLayerCommand(const TQString& name, KisLayerSP layer); + virtual ~KisLayerCommand() {} + + virtual void execute() = 0; + virtual void unexecute() = 0; + + protected: + void setUndo(bool undo); + + KisLayerSP m_layer; + }; + + KisLayerCommand::KisLayerCommand(const TQString& name, KisLayerSP layer) : + super(name), m_layer(layer) + { + } + + void KisLayerCommand::setUndo(bool undo) + { + if (m_layer->undoAdapter()) { + m_layer->undoAdapter()->setUndo(undo); + } + } + + class KisLayerLockedCommand : public KisLayerCommand { + typedef KisLayerCommand super; + + public: + KisLayerLockedCommand(KisLayerSP layer, bool newLocked); + + virtual void execute(); + virtual void unexecute(); + + private: + bool m_newLocked; + }; + + KisLayerLockedCommand::KisLayerLockedCommand(KisLayerSP layer, bool newLocked) : + super(i18n("Lock Layer"), layer) + { + m_newLocked = newLocked; + } + + void KisLayerLockedCommand::execute() + { + setUndo(false); + m_layer->setLocked(m_newLocked); + setUndo(true); + } + + void KisLayerLockedCommand::unexecute() + { + setUndo(false); + m_layer->setLocked(!m_newLocked); + setUndo(true); + } + + class KisLayerOpacityCommand : public KisLayerCommand { + typedef KisLayerCommand super; + + public: + KisLayerOpacityCommand(KisLayerSP layer, TQ_UINT8 oldOpacity, TQ_UINT8 newOpacity); + + virtual void execute(); + virtual void unexecute(); + + private: + TQ_UINT8 m_oldOpacity; + TQ_UINT8 m_newOpacity; + }; + + KisLayerOpacityCommand::KisLayerOpacityCommand(KisLayerSP layer, TQ_UINT8 oldOpacity, TQ_UINT8 newOpacity) : + super(i18n("Layer Opacity"), layer) + { + m_oldOpacity = oldOpacity; + m_newOpacity = newOpacity; + } + + void KisLayerOpacityCommand::execute() + { + setUndo(false); + m_layer->setOpacity(m_newOpacity); + setUndo(true); + } + + void KisLayerOpacityCommand::unexecute() + { + setUndo(false); + m_layer->setOpacity(m_oldOpacity); + setUndo(true); + } + + class KisLayerVisibilityCommand : public KisLayerCommand { + typedef KisLayerCommand super; + + public: + KisLayerVisibilityCommand(KisLayerSP layer, bool newVisibility); + + virtual void execute(); + virtual void unexecute(); + + private: + bool m_newVisibility; + }; + + KisLayerVisibilityCommand::KisLayerVisibilityCommand(KisLayerSP layer, bool newVisibility) : + super(i18n("Layer Visibility"), layer) + { + m_newVisibility = newVisibility; + } + + void KisLayerVisibilityCommand::execute() + { + setUndo(false); + m_layer->setVisible(m_newVisibility); + setUndo(true); + } + + void KisLayerVisibilityCommand::unexecute() + { + setUndo(false); + m_layer->setVisible(!m_newVisibility); + setUndo(true); + } + + class KisLayerCompositeOpCommand : public KisLayerCommand { + typedef KisLayerCommand super; + + public: + KisLayerCompositeOpCommand(KisLayerSP layer, const KisCompositeOp& oldCompositeOp, const KisCompositeOp& newCompositeOp); + + virtual void execute(); + virtual void unexecute(); + + private: + KisCompositeOp m_oldCompositeOp; + KisCompositeOp m_newCompositeOp; + }; + + KisLayerCompositeOpCommand::KisLayerCompositeOpCommand(KisLayerSP layer, const KisCompositeOp& oldCompositeOp, + const KisCompositeOp& newCompositeOp) : + super(i18n("Layer Composite Mode"), layer) + { + m_oldCompositeOp = oldCompositeOp; + m_newCompositeOp = newCompositeOp; + } + + void KisLayerCompositeOpCommand::execute() + { + setUndo(false); + m_layer->setCompositeOp(m_newCompositeOp); + setUndo(true); + } + + void KisLayerCompositeOpCommand::unexecute() + { + setUndo(false); + m_layer->setCompositeOp(m_oldCompositeOp); + setUndo(true); + } + + class KisLayerOffsetCommand : public KNamedCommand { + typedef KNamedCommand super; + + public: + KisLayerOffsetCommand(KisLayerSP layer, const TQPoint& oldpos, const TQPoint& newpos); + virtual ~KisLayerOffsetCommand(); + + virtual void execute(); + virtual void unexecute(); + + private: + void moveTo(const TQPoint& pos); + + private: + KisLayerSP m_layer; + TQRect m_updateRect; + TQPoint m_oldPos; + TQPoint m_newPos; + }; + + KisLayerOffsetCommand::KisLayerOffsetCommand(KisLayerSP layer, const TQPoint& oldpos, const TQPoint& newpos) : + super(i18n("Move Layer")) + { + m_layer = layer; + m_oldPos = oldpos; + m_newPos = newpos; + + TQRect currentBounds = m_layer->exactBounds(); + TQRect oldBounds = currentBounds; + oldBounds.moveBy(oldpos.x() - newpos.x(), oldpos.y() - newpos.y()); + + m_updateRect = currentBounds | oldBounds; + } + + KisLayerOffsetCommand::~KisLayerOffsetCommand() + { + } + + void KisLayerOffsetCommand::execute() + { + moveTo(m_newPos); + } + + void KisLayerOffsetCommand::unexecute() + { + moveTo(m_oldPos); + } + + void KisLayerOffsetCommand::moveTo(const TQPoint& pos) + { + if (m_layer->undoAdapter()) { + m_layer->undoAdapter()->setUndo(false); + } + + m_layer->setX(pos.x()); + m_layer->setY(pos.y()); + + m_layer->setDirty(m_updateRect); + + if (m_layer->undoAdapter()) { + m_layer->undoAdapter()->setUndo(true); + } + } +} + +static int getID() +{ + static int id = 1; + return id++; +} + + +KisLayer::KisLayer(KisImage *img, const TQString &name, TQ_UINT8 opacity) : + TQObject(0, name.latin1()), + TDEShared(), + m_id(getID()), + m_index(-1), + m_opacity(opacity), + m_locked(false), + m_visible(true), + m_temporary(false), + m_name(name), + m_parent(0), + m_image(img), + m_compositeOp(COMPOSITE_OVER) +{ +} + +KisLayer::KisLayer(const KisLayer& rhs) : + TQObject(), + TDEShared(rhs) +{ + if (this != &rhs) { + m_id = getID(); + m_index = -1; + m_opacity = rhs.m_opacity; + m_locked = rhs.m_locked; + m_visible = rhs.m_visible; + m_temporary = rhs.m_temporary; + m_dirtyRect = rhs.m_dirtyRect; + m_name = rhs.m_name; + m_image = rhs.m_image; + m_parent = 0; + m_compositeOp = rhs.m_compositeOp; + } +} + +KisLayer::~KisLayer() +{ +} + +void KisLayer::setClean(const TQRect & rect) +{ + if (m_dirtyRect.isValid() && rect.isValid()) { + + // XXX: We should only set the parts clean that were actually cleaned. However, extent and exactBounds conspire + // to make that very hard atm. + //if (rect.contains(m_dirtyRect)) m_dirtyRect = TQRect(); + m_dirtyRect = TQRect(); + } + +} + +bool KisLayer::dirty() +{ + return m_dirtyRect.isValid(); +} + + +bool KisLayer::dirty(const TQRect & rc) +{ + if (!m_dirtyRect.isValid() || !rc.isValid()) return false; + + return rc.intersects(m_dirtyRect); +} + +TQRect KisLayer::dirtyRect() const +{ + return m_dirtyRect; +} + +void KisLayer::setDirty(bool propagate) +{ + TQRect rc = extent(); + + if (rc.isValid()) m_dirtyRect = rc; + + // If we're dirty, our parent is dirty, if we've got a parent + if (propagate && m_parent && rc.isValid()) m_parent->setDirty(m_dirtyRect); + + if (m_image && rc.isValid()) { + m_image->notifyLayerUpdated(this, rc); + } +} + +void KisLayer::setDirty(const TQRect & rc, bool propagate) +{ + // If we're dirty, our parent is dirty, if we've got a parent + + if (rc.isValid()) + m_dirtyRect |= rc; + + if (propagate && m_parent && m_dirtyRect.isValid()) + m_parent->setDirty(m_dirtyRect); + + if (m_image && rc.isValid()) { + m_image->notifyLayerUpdated(this, rc); + } +} + +KisGroupLayerSP KisLayer::parent() const +{ + return m_parent; +} + +KisLayerSP KisLayer::prevSibling() const +{ + if (!parent()) + return 0; + return parent()->at(index() - 1); +} + +KisLayerSP KisLayer::nextSibling() const +{ + if (!parent()) + return 0; + return parent()->at(index() + 1); +} + +int KisLayer::index() const +{ + return m_index; +} + +void KisLayer::setIndex(int i) +{ + if (!parent()) + return; + parent()->setIndex(this, i); +} + +KisLayerSP KisLayer::findLayer(const TQString& n) const +{ + if (name() == n) + return const_cast(this); //HACK any less ugly way? findLayer() is conceptually const... + for (KisLayerSP layer = firstChild(); layer; layer = layer->nextSibling()) + if (KisLayerSP found = layer->findLayer(n)) + return found; + return 0; +} + +KisLayerSP KisLayer::findLayer(int i) const +{ + if (id() == i) + return const_cast(this); //HACK + for (KisLayerSP layer = firstChild(); layer; layer = layer->nextSibling()) + if (KisLayerSP found = layer->findLayer(i)) + return found; + return 0; +} + +int KisLayer::numLayers(int flags) const +{ + int num = 0; + if (matchesFlags(flags)) num++; + for (KisLayerSP layer = firstChild(); layer; layer = layer->nextSibling()) + num += layer->numLayers(flags); + return num; +} + +bool KisLayer::matchesFlags(int flags) const +{ + if ((flags & Visible) && !visible()) + return false; + if ((flags & Hidden) && visible()) + return false; + if ((flags & Locked) && !locked()) + return false; + if ((flags & Unlocked) && locked()) + return false; + return true; +} + +TQ_UINT8 KisLayer::opacity() const +{ + return m_opacity; +} + +void KisLayer::setOpacity(TQ_UINT8 val) +{ + if (m_opacity != val) + { + m_opacity = val; + setDirty(); + notifyPropertyChanged(); + } +} + +KNamedCommand *KisLayer::setOpacityCommand(TQ_UINT8 newOpacity) +{ + return new KisLayerOpacityCommand(this, opacity(), newOpacity); +} + +KNamedCommand *KisLayer::setOpacityCommand(TQ_UINT8 prevOpacity, TQ_UINT8 newOpacity) +{ + return new KisLayerOpacityCommand(this, prevOpacity, newOpacity); +} + +const bool KisLayer::visible() const +{ + return m_visible; +} + +void KisLayer::setVisible(bool v) +{ + if (m_visible != v) { + + m_visible = v; + notifyPropertyChanged(); + setDirty(); + + if (undoAdapter() && undoAdapter()->undo()) { + undoAdapter()->addCommand(setVisibleCommand(v)); + } + } +} + +KNamedCommand *KisLayer::setVisibleCommand(bool newVisibility) +{ + return new KisLayerVisibilityCommand(this, newVisibility); +} + +bool KisLayer::locked() const +{ + return m_locked; +} + +void KisLayer::setLocked(bool l) +{ + if (m_locked != l) { + m_locked = l; + notifyPropertyChanged(); + + if (undoAdapter() && undoAdapter()->undo()) { + undoAdapter()->addCommand(setLockedCommand(l)); + } + } +} + +bool KisLayer::temporary() const +{ + return m_temporary; +} + +void KisLayer::setTemporary(bool t) +{ + m_temporary = t; +} + +KNamedCommand *KisLayer::setLockedCommand(bool newLocked) +{ + return new KisLayerLockedCommand(this, newLocked); +} + +TQString KisLayer::name() const +{ + return m_name; +} + +void KisLayer::setName(const TQString& name) +{ + if (!name.isEmpty() && m_name != name) + { + m_name = name; + notifyPropertyChanged(); + } +} + +void KisLayer::setCompositeOp(const KisCompositeOp& compositeOp) +{ + if (m_compositeOp != compositeOp) + { + m_compositeOp = compositeOp; + notifyPropertyChanged(); + setDirty(); + + } +} + +KNamedCommand *KisLayer::setCompositeOpCommand(const KisCompositeOp& newCompositeOp) +{ + return new KisLayerCompositeOpCommand(this, compositeOp(), newCompositeOp); +} + +KNamedCommand *KisLayer::moveCommand(TQPoint oldPosition, TQPoint newPosition) +{ + return new KisLayerOffsetCommand(this, oldPosition, newPosition); +} + +KisUndoAdapter *KisLayer::undoAdapter() const +{ + if (m_image) { + return m_image->undoAdapter(); + } + return 0; +} + +void KisLayer::paintMaskInactiveLayers(TQImage &, TQ_INT32, TQ_INT32, TQ_INT32, TQ_INT32) +{ +} + +void KisLayer::paintSelection(TQImage &, TQ_INT32, TQ_INT32, TQ_INT32, TQ_INT32) +{ +} + +void KisLayer::paintSelection(TQImage &, const TQRect&, const TQSize&, const TQSize&) +{ +} + +TQImage KisLayer::createThumbnail(TQ_INT32, TQ_INT32) +{ + return 0; +} + +void KisLayer::notifyPropertyChanged() +{ + if(image() && !signalsBlocked()) + image()->notifyPropertyChanged(this); +} + +void KisLayerSupportsIndirectPainting::setTemporaryTarget(KisPaintDeviceSP t) { + m_temporaryTarget = t; +} + +void KisLayerSupportsIndirectPainting::setTemporaryCompositeOp(const KisCompositeOp& c) { + m_compositeOp = c; +} + +void KisLayerSupportsIndirectPainting::setTemporaryOpacity(TQ_UINT8 o) { + m_compositeOpacity = o; +} + +KisPaintDeviceSP KisLayerSupportsIndirectPainting::temporaryTarget() { + return m_temporaryTarget; +} + +KisCompositeOp KisLayerSupportsIndirectPainting::temporaryCompositeOp() const { + return m_compositeOp; +} + +TQ_UINT8 KisLayerSupportsIndirectPainting::temporaryOpacity() const { + return m_compositeOpacity; +} + +#include "kis_layer.moc" diff --git a/chalk/core/kis_meta_registry.cc b/chalk/core/kis_meta_registry.cc deleted file mode 100644 index 4bde87b2..00000000 --- a/chalk/core/kis_meta_registry.cc +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2005 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include -#include -#include - -#include -#include LCMS_HEADER - -#include -#include -#include - -KisMetaRegistry * KisMetaRegistry::m_singleton = 0; - -KisMetaRegistry::KisMetaRegistry() -{ - // Create the colorspaces and load the profiles - - TDEGlobal::instance()->dirs()->addResourceType("kis_profiles", - TDEStandardDirs::kde_default("data") + "chalk/profiles/"); - - // Add those things here as well, since we are not yet using KisDoc's KisFactory instance (which inits these as well) - TDEGlobal::instance()->dirs()->addResourceType("kis_profiles", TDEStandardDirs::kde_default("data") + "chalk/profiles/"); - TDEGlobal::instance()->dirs()->addResourceDir("kis_profiles", "/usr/share/color/icc"); - TDEGlobal::instance()->dirs()->addResourceDir("kis_profiles", TQDir::homeDirPath() + TQString("/.icc/")); - TDEGlobal::instance()->dirs()->addResourceDir("kis_profiles", TQDir::homeDirPath() + TQString("/.color/icc/")); - - TQStringList profileFilenames; - profileFilenames += TDEGlobal::instance()->dirs()->findAllResources("kis_profiles", "*.icm", true /* recursive */); - profileFilenames += TDEGlobal::instance()->dirs()->findAllResources("kis_profiles", "*.ICM", true); - profileFilenames += TDEGlobal::instance()->dirs()->findAllResources("kis_profiles", "*.ICC", true); - profileFilenames += TDEGlobal::instance()->dirs()->findAllResources("kis_profiles", "*.icc", true); - // Set lcms to return NUll/false etc from failing calls, rather than aborting the app. - cmsErrorAction(LCMS_ERROR_SHOW); - - m_csRegistry = new KisColorSpaceFactoryRegistry(profileFilenames); - m_mtRegistry = new KisMathToolboxFactoryRegistry(); -} - -KisMetaRegistry::~KisMetaRegistry() -{ -} - -KisMetaRegistry * KisMetaRegistry::instance() -{ - if ( KisMetaRegistry::m_singleton == 0 ) { - KisMetaRegistry::m_singleton = new KisMetaRegistry(); - } - return KisMetaRegistry::m_singleton; -} - diff --git a/chalk/core/kis_meta_registry.cpp b/chalk/core/kis_meta_registry.cpp new file mode 100644 index 00000000..4bde87b2 --- /dev/null +++ b/chalk/core/kis_meta_registry.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include + +#include +#include LCMS_HEADER + +#include +#include +#include + +KisMetaRegistry * KisMetaRegistry::m_singleton = 0; + +KisMetaRegistry::KisMetaRegistry() +{ + // Create the colorspaces and load the profiles + + TDEGlobal::instance()->dirs()->addResourceType("kis_profiles", + TDEStandardDirs::kde_default("data") + "chalk/profiles/"); + + // Add those things here as well, since we are not yet using KisDoc's KisFactory instance (which inits these as well) + TDEGlobal::instance()->dirs()->addResourceType("kis_profiles", TDEStandardDirs::kde_default("data") + "chalk/profiles/"); + TDEGlobal::instance()->dirs()->addResourceDir("kis_profiles", "/usr/share/color/icc"); + TDEGlobal::instance()->dirs()->addResourceDir("kis_profiles", TQDir::homeDirPath() + TQString("/.icc/")); + TDEGlobal::instance()->dirs()->addResourceDir("kis_profiles", TQDir::homeDirPath() + TQString("/.color/icc/")); + + TQStringList profileFilenames; + profileFilenames += TDEGlobal::instance()->dirs()->findAllResources("kis_profiles", "*.icm", true /* recursive */); + profileFilenames += TDEGlobal::instance()->dirs()->findAllResources("kis_profiles", "*.ICM", true); + profileFilenames += TDEGlobal::instance()->dirs()->findAllResources("kis_profiles", "*.ICC", true); + profileFilenames += TDEGlobal::instance()->dirs()->findAllResources("kis_profiles", "*.icc", true); + // Set lcms to return NUll/false etc from failing calls, rather than aborting the app. + cmsErrorAction(LCMS_ERROR_SHOW); + + m_csRegistry = new KisColorSpaceFactoryRegistry(profileFilenames); + m_mtRegistry = new KisMathToolboxFactoryRegistry(); +} + +KisMetaRegistry::~KisMetaRegistry() +{ +} + +KisMetaRegistry * KisMetaRegistry::instance() +{ + if ( KisMetaRegistry::m_singleton == 0 ) { + KisMetaRegistry::m_singleton = new KisMetaRegistry(); + } + return KisMetaRegistry::m_singleton; +} + diff --git a/chalk/core/kis_nameserver.cc b/chalk/core/kis_nameserver.cc deleted file mode 100644 index 4cb51a98..00000000 --- a/chalk/core/kis_nameserver.cc +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_nameserver.h" - -KisNameServer::KisNameServer(const TQString& prefix, TQ_INT32 seed) -{ - m_prefix = prefix; - m_generator = seed; -} - -KisNameServer::~KisNameServer() -{ -} - -TQString KisNameServer::name() -{ - return m_prefix.arg(m_generator++); -} - -TQ_INT32 KisNameServer::currentSeed() const -{ - return m_generator; -} - -TQ_INT32 KisNameServer::number() -{ - return m_generator++; -} - -void KisNameServer::rollback() -{ - m_generator--; -} - diff --git a/chalk/core/kis_nameserver.cpp b/chalk/core/kis_nameserver.cpp new file mode 100644 index 00000000..4cb51a98 --- /dev/null +++ b/chalk/core/kis_nameserver.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002 Patrick Julien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_nameserver.h" + +KisNameServer::KisNameServer(const TQString& prefix, TQ_INT32 seed) +{ + m_prefix = prefix; + m_generator = seed; +} + +KisNameServer::~KisNameServer() +{ +} + +TQString KisNameServer::name() +{ + return m_prefix.arg(m_generator++); +} + +TQ_INT32 KisNameServer::currentSeed() const +{ + return m_generator; +} + +TQ_INT32 KisNameServer::number() +{ + return m_generator++; +} + +void KisNameServer::rollback() +{ + m_generator--; +} + diff --git a/chalk/core/kis_paint_device.cc b/chalk/core/kis_paint_device.cc deleted file mode 100644 index da25caec..00000000 --- a/chalk/core/kis_paint_device.cc +++ /dev/null @@ -1,1285 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * Copyright (c) 2004 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "kis_global.h" -#include "kis_types.h" -#include "kis_painter.h" -#include "kis_fill_painter.h" -#include "kis_undo_adapter.h" -#include "kis_iterator.h" -#include "kis_iterators_pixel.h" -#include "kis_iteratorpixeltrait.h" -#include "kis_random_accessor.h" -#include "kis_random_sub_accessor.h" -#include "kis_transaction.h" -#include "kis_profile.h" -#include "kis_color.h" -#include "kis_integer_maths.h" -#include "kis_colorspace_factory_registry.h" -#include "kis_selection.h" -#include "kis_layer.h" -#include "kis_paint_device_iface.h" -#include "kis_paint_device.h" -#include "kis_datamanager.h" -#include "kis_memento.h" -#include "kis_selection.h" - -#include "kis_exif_info.h" - -namespace { - - class KisPaintDeviceCommand : public KNamedCommand { - typedef KNamedCommand super; - - public: - KisPaintDeviceCommand(const TQString& name, KisPaintDeviceSP paintDevice); - virtual ~KisPaintDeviceCommand() {} - - virtual void execute() = 0; - virtual void unexecute() = 0; - - protected: - void setUndo(bool undo); - - KisPaintDeviceSP m_paintDevice; - }; - - KisPaintDeviceCommand::KisPaintDeviceCommand(const TQString& name, KisPaintDeviceSP paintDevice) : - super(name), m_paintDevice(paintDevice) - { - } - - void KisPaintDeviceCommand::setUndo(bool undo) - { - if (m_paintDevice->undoAdapter()) { - m_paintDevice->undoAdapter()->setUndo(undo); - } - } - - class MoveCommand : public KNamedCommand { - typedef KNamedCommand super; - - public: - MoveCommand(KisPaintDeviceSP device, const TQPoint& oldpos, const TQPoint& newpos); - virtual ~MoveCommand(); - - virtual void execute(); - virtual void unexecute(); - - private: - void moveTo(const TQPoint& pos); - void undoOff(); - void undoOn(); - - private: - KisPaintDeviceSP m_device; - TQPoint m_oldPos; - TQPoint m_newPos; - }; - - MoveCommand::MoveCommand(KisPaintDeviceSP device, const TQPoint& oldpos, const TQPoint& newpos) : - super(i18n("Move Layer")) - { - m_device = device; - m_oldPos = oldpos; - m_newPos = newpos; - } - - MoveCommand::~MoveCommand() - { - } - - void MoveCommand::undoOff() - { - if (m_device->undoAdapter()) { - m_device->undoAdapter()->setUndo(false); - } - } - - void MoveCommand::undoOn() - { - if (m_device->undoAdapter()) { - m_device->undoAdapter()->setUndo(true); - } - } - - void MoveCommand::execute() - { - undoOff(); - moveTo(m_newPos); - undoOn(); - } - - void MoveCommand::unexecute() - { - undoOff(); - moveTo(m_oldPos); - undoOn(); - } - - void MoveCommand::moveTo(const TQPoint& pos) - { - m_device->move(pos.x(), pos.y()); - } - - class KisConvertLayerTypeCmd : public KNamedCommand { - typedef KNamedCommand super; - - public: - KisConvertLayerTypeCmd(KisUndoAdapter *adapter, KisPaintDeviceSP paintDevice, - KisDataManagerSP beforeData, KisColorSpace * beforeColorSpace, - KisDataManagerSP afterData, KisColorSpace * afterColorSpace - ) : super(i18n("Convert Layer Type")) - { - m_adapter = adapter; - m_paintDevice = paintDevice; - m_beforeData = beforeData; - m_beforeColorSpace = beforeColorSpace; - m_afterData = afterData; - m_afterColorSpace = afterColorSpace; - } - - virtual ~KisConvertLayerTypeCmd() - { - } - - public: - virtual void execute() - { - m_adapter->setUndo(false); - m_paintDevice->setData(m_afterData, m_afterColorSpace); - m_adapter->setUndo(true); - } - - virtual void unexecute() - { - m_adapter->setUndo(false); - m_paintDevice->setData(m_beforeData, m_beforeColorSpace); - m_adapter->setUndo(true); - } - - private: - KisUndoAdapter *m_adapter; - - KisPaintDeviceSP m_paintDevice; - - KisDataManagerSP m_beforeData; - KisColorSpace * m_beforeColorSpace; - - KisDataManagerSP m_afterData; - KisColorSpace * m_afterColorSpace; - }; - -} - -KisPaintDevice::KisPaintDevice(KisColorSpace * colorSpace, const char * name) : - TQObject(0, name), TDEShared(), m_exifInfo(0), m_lock( false ) -{ - if (colorSpace == 0) { - kdWarning(41001) << "Cannot create paint device without colorstrategy!\n"; - return; - } - m_longRunningFilterTimer = 0; - m_dcop = 0; - - m_x = 0; - m_y = 0; - - m_pixelSize = colorSpace->pixelSize(); - m_nChannels = colorSpace->nChannels(); - - TQ_UINT8* defPixel = new TQ_UINT8 [ m_pixelSize ]; - colorSpace->fromTQColor(TQt::black, OPACITY_TRANSPARENT, defPixel); - - m_datamanager = new KisDataManager(m_pixelSize, defPixel); - delete [] defPixel; - - TQ_CHECK_PTR(m_datamanager); - m_extentIsValid = true; - - m_parentLayer = 0; - - m_colorSpace = colorSpace; - - m_hasSelection = false; - m_selectionDeselected = false; - m_selection = 0; - -} - -KisPaintDevice::KisPaintDevice(KisLayer *parent, KisColorSpace * colorSpace, const char * name) : - TQObject(0, name), TDEShared(), m_exifInfo(0), m_lock( false ) -{ - - m_longRunningFilterTimer = 0; - m_dcop = 0; - - m_x = 0; - m_y = 0; - - m_hasSelection = false; - m_selectionDeselected = false; - m_selection = 0; - - m_parentLayer = parent; - - if (colorSpace == 0 && parent != 0 && parent->image() != 0) { - m_colorSpace = parent->image()->colorSpace(); - } - else { - m_colorSpace = colorSpace; - } - - Q_ASSERT( m_colorSpace ); - - m_pixelSize = m_colorSpace->pixelSize(); - m_nChannels = m_colorSpace->nChannels(); - - TQ_UINT8* defPixel = new TQ_UINT8[ m_pixelSize ]; - m_colorSpace->fromTQColor(TQt::black, OPACITY_TRANSPARENT, defPixel); - - m_datamanager = new KisDataManager(m_pixelSize, defPixel); - delete [] defPixel; - TQ_CHECK_PTR(m_datamanager); - m_extentIsValid = true; - - if ( TQString ( name ) == TQString( "Layer 1" ) ) { - m_longRunningFilters = m_colorSpace->createBackgroundFilters(); - - if (!m_longRunningFilters.isEmpty()) { - m_longRunningFilterTimer = new TQTimer(this); - connect(m_longRunningFilterTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(runBackgroundFilters())); - m_longRunningFilterTimer->start(2000); - } - } -} - - -KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs) : TQObject(), TDEShared(rhs) -{ - if (this != &rhs) { - m_longRunningFilterTimer = 0; - m_parentLayer = 0; - m_dcop = rhs.m_dcop; - if (rhs.m_datamanager) { - m_datamanager = new KisDataManager(*rhs.m_datamanager); - TQ_CHECK_PTR(m_datamanager); - } - else { - kdWarning() << "rhs " << rhs.name() << " has no datamanager\n"; - } - m_extentIsValid = rhs.m_extentIsValid; - m_x = rhs.m_x; - m_y = rhs.m_y; - m_colorSpace = rhs.m_colorSpace; - m_hasSelection = rhs.m_hasSelection; - - if ( m_hasSelection ) - m_selection = new KisSelection(*rhs.m_selection); - else - m_selection = 0; - - m_pixelSize = rhs.m_pixelSize; - m_nChannels = rhs.m_nChannels; - if(rhs.m_exifInfo) - { - m_exifInfo = new KisExifInfo(*rhs.m_exifInfo); - } - else { - m_exifInfo = 0; - } - } -} - -KisPaintDevice::~KisPaintDevice() -{ - delete m_dcop; - delete m_longRunningFilterTimer; - TQValueList::iterator it; - TQValueList::iterator end = m_longRunningFilters.end(); - for (it = m_longRunningFilters.begin(); it != end; ++it) { - KisFilter * f = (*it); - delete f; - } - m_longRunningFilters.clear(); - //delete m_exifInfo; -} - -DCOPObject *KisPaintDevice::dcopObject() -{ - if (!m_dcop) { - m_dcop = new KisPaintDeviceIface(this); - TQ_CHECK_PTR(m_dcop); - } - return m_dcop; -} - -KisLayer *KisPaintDevice::parentLayer() const -{ - return m_parentLayer; -} - -void KisPaintDevice::setParentLayer(KisLayer *parentLayer) -{ - m_parentLayer = parentLayer; -} - -void KisPaintDevice::setDirty(const TQRect & rc) -{ - if (m_parentLayer) m_parentLayer->setDirty(rc); -} - -void KisPaintDevice::setDirty() -{ - if (m_parentLayer) m_parentLayer->setDirty(); -} - -KisImage *KisPaintDevice::image() const -{ - if (m_parentLayer) { - return m_parentLayer->image(); - } else { - return 0; - } -} - - -void KisPaintDevice::move(TQ_INT32 x, TQ_INT32 y) -{ - TQRect dirtyRect = extent(); - - m_x = x; - m_y = y; - - dirtyRect |= extent(); - - if(m_selection) - { - m_selection->setX(x); - m_selection->setY(y); - } - - setDirty(dirtyRect); - - emit positionChanged(this); -} - -void KisPaintDevice::move(const TQPoint& pt) -{ - move(pt.x(), pt.y()); -} - -KNamedCommand * KisPaintDevice::moveCommand(TQ_INT32 x, TQ_INT32 y) -{ - KNamedCommand * cmd = new MoveCommand(this, TQPoint(m_x, m_y), TQPoint(x, y)); - TQ_CHECK_PTR(cmd); - cmd->execute(); - return cmd; -} - -void KisPaintDevice::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const -{ - m_datamanager->extent(x, y, w, h); - x += m_x; - y += m_y; -} - -TQRect KisPaintDevice::extent() const -{ - TQ_INT32 x, y, w, h; - extent(x, y, w, h); - return TQRect(x, y, w, h); -} - -bool KisPaintDevice::extentIsValid() const -{ - return m_extentIsValid; -} - -void KisPaintDevice::setExtentIsValid(bool isValid) -{ - m_extentIsValid = isValid; -} - -void KisPaintDevice::exactBounds(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const -{ - TQRect r = exactBounds(); - x = r.x(); - y = r.y(); - w = r.width(); - h = r.height(); -} - -TQRect KisPaintDevice::exactBoundsOldMethod() const -{ - TQ_INT32 x, y, w, h, boundX, boundY, boundW, boundH; - extent(x, y, w, h); - - extent(boundX, boundY, boundW, boundH); - - const TQ_UINT8* defaultPixel = m_datamanager->defaultPixel(); - - bool found = false; - - for (TQ_INT32 y2 = y; y2 < y + h ; ++y2) { - KisHLineIterator it = const_cast(this)->createHLineIterator(x, y2, w, false); - while (!it.isDone() && found == false) { - if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { - boundY = y2; - found = true; - break; - } - ++it; - } - if (found) break; - } - - found = false; - - for (TQ_INT32 y2 = y + h; y2 > y ; --y2) { - KisHLineIterator it = const_cast(this)->createHLineIterator(x, y2, w, false); - while (!it.isDone() && found == false) { - if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { - boundH = y2 - boundY + 1; - found = true; - break; - } - ++it; - } - if (found) break; - } - found = false; - - for (TQ_INT32 x2 = x; x2 < x + w ; ++x2) { - KisVLineIterator it = const_cast(this)->createVLineIterator(x2, y, h, false); - while (!it.isDone() && found == false) { - if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { - boundX = x2; - found = true; - break; - } - ++it; - } - if (found) break; - } - - found = false; - - // Look for right edge ) - for (TQ_INT32 x2 = x + w; x2 > x ; --x2) { - KisVLineIterator it = const_cast(this)->createVLineIterator(x2, y, h, false); - while (!it.isDone() && found == false) { - if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { - boundW = x2 - boundX + 1; // XXX: I commented this - // +1 out, but why? It - // should be correct, since - // we've found the first - // pixel that should be - // included, and it should - // be added to the width. - found = true; - break; - } - ++it; - } - if (found) break; - } - - return TQRect(boundX, boundY, boundW, boundH); -} - -TQRect KisPaintDevice::exactBoundsImprovedOldMethod() const -{ - // Solution n°2 - TQ_INT32 x, y, w, h, boundX2, boundY2, boundW2, boundH2; - extent(x, y, w, h); - extent(boundX2, boundY2, boundW2, boundH2); - - const TQ_UINT8* defaultPixel = m_datamanager->defaultPixel(); - bool found = false; - { - KisHLineIterator it = const_cast(this)->createHLineIterator(x, y, w, false); - for (TQ_INT32 y2 = y; y2 < y + h ; ++y2) { - while (!it.isDone() && found == false) { - if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { - boundY2 = y2; - found = true; - break; - } - ++it; - } - if (found) break; - it.nextRow(); - } - } - - found = false; - - for (TQ_INT32 y2 = y + h; y2 > y ; --y2) { - KisHLineIterator it = const_cast(this)->createHLineIterator(x, y2, w, false); - while (!it.isDone() && found == false) { - if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { - boundH2 = y2 - boundY2 + 1; - found = true; - break; - } - ++it; - } - if (found) break; - } - found = false; - - { - KisVLineIterator it = const_cast(this)->createVLineIterator(x, boundY2, boundH2, false); - for (TQ_INT32 x2 = x; x2 < x + w ; ++x2) { - while (!it.isDone() && found == false) { - if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { - boundX2 = x2; - found = true; - break; - } - ++it; - } - if (found) break; - it.nextCol(); - } - } - - found = false; - - // Look for right edge ) - { - for (TQ_INT32 x2 = x + w; x2 > x ; --x2) { - KisVLineIterator it = const_cast(this)->createVLineIterator(/*x + w*/ x2, boundY2, boundH2, false); - while (!it.isDone() && found == false) { - if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { - boundW2 = x2 - boundX2 + 1; // XXX: I commented this - // +1 out, but why? It - // should be correct, since - // we've found the first - // pixel that should be - // included, and it should - // be added to the width. - found = true; - break; - } - ++it; - } - if (found) break; - } - } - return TQRect(boundX2, boundY2, boundW2, boundH2); -} - - -TQRect KisPaintDevice::exactBounds() const -{ - TQRect r2 = exactBoundsImprovedOldMethod(); - return r2; -} - -void KisPaintDevice::crop(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - m_datamanager->setExtent(x - m_x, y - m_y, w, h); -} - - -void KisPaintDevice::crop(TQRect r) -{ - r.moveBy(-m_x, -m_y); m_datamanager->setExtent(r); -} - -void KisPaintDevice::clear() -{ - m_datamanager->clear(); -} - -void KisPaintDevice::fill(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const TQ_UINT8 *fillPixel) -{ - m_datamanager->clear(x, y, w, h, fillPixel); -} - -void KisPaintDevice::mirrorX() -{ - TQRect r; - if (hasSelection()) { - r = selection()->selectedRect(); - } - else { - r = exactBounds(); - } - - for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { - KisHLineIteratorPixel srcIt = createHLineIterator(r.x(), y, r.width(), false); - KisHLineIteratorPixel dstIt = createHLineIterator(r.x(), y, r.width(), true); - - dstIt += r.width() - 1; - - while (!srcIt.isDone()) { - if (srcIt.isSelected()) { - memcpy(dstIt.rawData(), srcIt.oldRawData(), m_pixelSize); - } - ++srcIt; - --dstIt; - - } - } - if (m_parentLayer) { - m_parentLayer->setDirty(r); - } -} - -void KisPaintDevice::mirrorY() -{ - /* Read a line from bottom to top and and from top to bottom and write their values to each other */ - TQRect r; - if (hasSelection()) { - r = selection()->selectedRect(); - } - else { - r = exactBounds(); - } - - - TQ_INT32 y1, y2; - for (y1 = r.top(), y2 = r.bottom(); y1 <= r.bottom(); ++y1, --y2) { - KisHLineIteratorPixel itTop = createHLineIterator(r.x(), y1, r.width(), true); - KisHLineIteratorPixel itBottom = createHLineIterator(r.x(), y2, r.width(), false); - while (!itTop.isDone() && !itBottom.isDone()) { - if (itBottom.isSelected()) { - memcpy(itTop.rawData(), itBottom.oldRawData(), m_pixelSize); - } - ++itBottom; - ++itTop; - } - } - - if (m_parentLayer) { - m_parentLayer->setDirty(r); - } -} - -KisMementoSP KisPaintDevice::getMemento() -{ - return m_datamanager->getMemento(); -} - -void KisPaintDevice::rollback(KisMementoSP memento) { m_datamanager->rollback(memento); } - -void KisPaintDevice::rollforward(KisMementoSP memento) { m_datamanager->rollforward(memento); } - -bool KisPaintDevice::write(KoStore *store) -{ - bool retval = m_datamanager->write(store); - emit ioProgress(100); - - return retval; -} - -bool KisPaintDevice::read(KoStore *store) -{ - bool retval = m_datamanager->read(store); - emit ioProgress(100); - - return retval; -} - -void KisPaintDevice::convertTo(KisColorSpace * dstColorSpace, TQ_INT32 renderingIntent) -{ - kdDebug(41004) << "Converting " << name() << " to " << dstColorSpace->id().id() << " from " - << m_colorSpace->id().id() << "\n"; - if ( colorSpace() == dstColorSpace ) - { - return; - } - - KisPaintDevice dst(dstColorSpace); - dst.setX(getX()); - dst.setY(getY()); - - TQ_INT32 x, y, w, h; - extent(x, y, w, h); - - for (TQ_INT32 row = y; row < y + h; ++row) { - - TQ_INT32 column = x; - TQ_INT32 columnsRemaining = w; - - while (columnsRemaining > 0) { - - TQ_INT32 numContiguousDstColumns = dst.numContiguousColumns(column, row, row); - TQ_INT32 numContiguousSrcColumns = numContiguousColumns(column, row, row); - - TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns); - columns = TQMIN(columns, columnsRemaining); - - //const TQ_UINT8 *srcData = pixel(column, row); - //TQ_UINT8 *dstData = dst.writablePixel(column, row); - KisHLineIteratorPixel srcIt = createHLineIterator(column, row, columns, false); - KisHLineIteratorPixel dstIt = dst.createHLineIterator(column, row, columns, true); - - const TQ_UINT8 *srcData = srcIt.rawData(); - TQ_UINT8 *dstData = dstIt.rawData(); - - - m_colorSpace->convertPixelsTo(srcData, dstData, dstColorSpace, columns, renderingIntent); - - column += columns; - columnsRemaining -= columns; - } - } - - KisDataManagerSP oldData = m_datamanager; - KisColorSpace *oldColorSpace = m_colorSpace; - - setData(dst.m_datamanager, dstColorSpace); - - if (undoAdapter() && undoAdapter()->undo()) { - undoAdapter()->addCommand(new KisConvertLayerTypeCmd(undoAdapter(), this, oldData, oldColorSpace, m_datamanager, m_colorSpace)); - } -} - -void KisPaintDevice::setProfile(KisProfile * profile) -{ - if (profile == 0) return; - - KisColorSpace * dstSpace = - KisMetaRegistry::instance()->csRegistry()->getColorSpace( colorSpace()->id(), - profile); - if (dstSpace) - m_colorSpace = dstSpace; - -} - -void KisPaintDevice::setData(KisDataManagerSP data, KisColorSpace * colorSpace) -{ - m_datamanager = data; - m_colorSpace = colorSpace; - m_pixelSize = m_colorSpace->pixelSize(); - m_nChannels = m_colorSpace->nChannels(); - - if (m_parentLayer) { - m_parentLayer->setDirty(extent()); - m_parentLayer->notifyPropertyChanged(); - } -} - -KisUndoAdapter *KisPaintDevice::undoAdapter() const -{ - if (m_parentLayer && m_parentLayer->image()) { - return m_parentLayer->image()->undoAdapter(); - } - return 0; -} - -void KisPaintDevice::convertFromTQImage(const TQImage& image, const TQString &srcProfileName, - TQ_INT32 offsetX, TQ_INT32 offsetY) -{ - TQImage img = image; - - // Chalk is little-endian inside. - if (img.bitOrder() == TQImage::LittleEndian) { - img = img.convertBitOrder(TQImage::BigEndian); - } - kdDebug() << k_funcinfo << img.bitOrder()<< endl; - // Chalk likes bgra (convertDepth returns *this is the img is alread 32 bits) - img = img.convertDepth( 32 ); -#if 0 - // XXX: Apply import profile - if (colorSpace() == KisMetaRegistry::instance()->csRegistry() ->getColorSpace(KisID("RGBA",""),"")) { - writeBytes(img.bits(), 0, 0, img.width(), img.height()); - } - else { -#endif - TQ_UINT8 * dstData = new TQ_UINT8[img.width() * img.height() * pixelSize()]; - KisMetaRegistry::instance()->csRegistry() - ->getColorSpace(KisID("RGBA",""),srcProfileName)-> - convertPixelsTo(img.bits(), dstData, colorSpace(), img.width() * img.height()); - writeBytes(dstData, offsetX, offsetY, img.width(), img.height()); -// } -} - -TQImage KisPaintDevice::convertToTQImage(KisProfile * dstProfile, float exposure) -{ - TQ_INT32 x1; - TQ_INT32 y1; - TQ_INT32 w; - TQ_INT32 h; - - x1 = - getX(); - y1 = - getY(); - - if (image()) { - w = image()->width(); - h = image()->height(); - } - else { - extent(x1, y1, w, h); - } - - return convertToTQImage(dstProfile, x1, y1, w, h, exposure); -} - -// XXX: is this faster than building the TQImage ourselves? It makes -TQImage KisPaintDevice::convertToTQImage(KisProfile * dstProfile, TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, float exposure) -{ - if (w < 0) - return TQImage(); - - if (h < 0) - return TQImage(); - - TQ_UINT8 * data = new TQ_UINT8 [w * h * m_pixelSize]; - TQ_CHECK_PTR(data); - - // XXX: Is this really faster than converting line by line and building the TQImage directly? - // This copies potentially a lot of data. - readBytes(data, x1, y1, w, h); - TQImage image = colorSpace()->convertToTQImage(data, w, h, dstProfile, INTENT_PERCEPTUAL, exposure); - delete[] data; - - return image; -} - -KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(TQ_INT32 w, TQ_INT32 h) -{ - KisPaintDeviceSP thumbnail = new KisPaintDevice(colorSpace(), "thumbnail"); - - thumbnail->clear(); - - int srcw, srch; - if( image() ) - { - srcw = image()->width(); - srch = image()->height(); - } - else - { - const TQRect e = exactBounds(); - srcw = e.width(); - srch = e.height(); - } - - if (w > srcw) - { - w = srcw; - h = TQ_INT32(double(srcw) / w * h); - } - if (h > srch) - { - h = srch; - w = TQ_INT32(double(srch) / h * w); - } - - if (srcw > srch) - h = TQ_INT32(double(srch) / srcw * w); - else if (srch > srcw) - w = TQ_INT32(double(srcw) / srch * h); - - for (TQ_INT32 y=0; y < h; ++y) { - TQ_INT32 iY = (y * srch ) / h; - for (TQ_INT32 x=0; x < w; ++x) { - TQ_INT32 iX = (x * srcw ) / w; - thumbnail->setPixel(x, y, colorAt(iX, iY)); - } - } - - return thumbnail; - -} - - -TQImage KisPaintDevice::createThumbnail(TQ_INT32 w, TQ_INT32 h) -{ - int srcw, srch; - if( image() ) - { - srcw = image()->width(); - srch = image()->height(); - } - else - { - const TQRect e = extent(); - srcw = e.width(); - srch = e.height(); - } - - if (w > srcw) - { - w = srcw; - h = TQ_INT32(double(srcw) / w * h); - } - if (h > srch) - { - h = srch; - w = TQ_INT32(double(srch) / h * w); - } - - if (srcw > srch) - h = TQ_INT32(double(srch) / srcw * w); - else if (srch > srcw) - w = TQ_INT32(double(srcw) / srch * h); - - TQColor c; - TQ_UINT8 opacity; - TQImage img(w,h,32); - - for (TQ_INT32 y=0; y < h; ++y) { - TQ_INT32 iY = (y * srch ) / h; - for (TQ_INT32 x=0; x < w; ++x) { - TQ_INT32 iX = (x * srcw ) / w; - pixel(iX, iY, &c, &opacity); - const TQRgb rgb = c.rgb(); - img.setPixel(x, y, tqRgba(tqRed(rgb), tqGreen(rgb), tqBlue(rgb), opacity)); - } - } - - return img; -} - -KisRectIteratorPixel KisPaintDevice::createRectIterator(TQ_INT32 left, TQ_INT32 top, TQ_INT32 w, TQ_INT32 h, bool writable) -{ - if(hasSelection()) - return KisRectIteratorPixel(this, m_datamanager, m_selection->m_datamanager, left, top, w, h, m_x, m_y, writable); - else - return KisRectIteratorPixel(this, m_datamanager, NULL, left, top, w, h, m_x, m_y, writable); -} - -KisHLineIteratorPixel KisPaintDevice::createHLineIterator(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable) -{ - if(hasSelection()) - return KisHLineIteratorPixel(this, m_datamanager, m_selection->m_datamanager, x, y, w, m_x, m_y, writable); - else - return KisHLineIteratorPixel(this, m_datamanager, NULL, x, y, w, m_x, m_y, writable); -} - -KisVLineIteratorPixel KisPaintDevice::createVLineIterator(TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable) -{ - if(hasSelection()) - return KisVLineIteratorPixel(this, m_datamanager, m_selection->m_datamanager, x, y, h, m_x, m_y, writable); - else - return KisVLineIteratorPixel(this, m_datamanager, NULL, x, y, h, m_x, m_y, writable); - -} - -KisRandomAccessorPixel KisPaintDevice::createRandomAccessor(TQ_INT32 x, TQ_INT32 y, bool writable) { - if(hasSelection()) - return KisRandomAccessorPixel(m_datamanager, m_selection->m_datamanager, x, y, m_x, m_y, writable); - else - return KisRandomAccessorPixel(m_datamanager, NULL, x, y, m_x, m_y, writable); -} - -KisRandomSubAccessorPixel KisPaintDevice::createRandomSubAccessor() -{ - return KisRandomSubAccessorPixel(this); -} - -void KisPaintDevice::emitSelectionChanged() -{ - if (m_parentLayer && m_parentLayer->image()) { - m_parentLayer->image()->slotSelectionChanged(); - } -} - -void KisPaintDevice::emitSelectionChanged(const TQRect& r) -{ - if (m_parentLayer && m_parentLayer->image()) { - m_parentLayer->image()->slotSelectionChanged(r); - } -} - -KisSelectionSP KisPaintDevice::selection() -{ - if ( m_selectionDeselected && m_selection ) { - m_selectionDeselected = false; - } - else if (!m_selection) { - m_selection = new KisSelection(this); - TQ_CHECK_PTR(m_selection); - m_selection->setX(m_x); - m_selection->setY(m_y); - } - m_hasSelection = true; - - return m_selection; -} - - -bool KisPaintDevice::hasSelection() -{ - return m_hasSelection; -} - -bool KisPaintDevice::selectionDeselected() -{ - return m_selectionDeselected; -} - - -void KisPaintDevice::deselect() -{ - if (m_selection && m_hasSelection) { - m_hasSelection = false; - m_selectionDeselected = true; - } -} - -void KisPaintDevice::reselect() -{ - m_hasSelection = true; - m_selectionDeselected = false; -} - -void KisPaintDevice::addSelection(KisSelectionSP selection) { - - KisPainter painter(this->selection().data()); - TQRect r = selection->selectedExactRect(); - painter.bitBlt(r.x(), r.y(), COMPOSITE_OVER, selection.data(), r.x(), r.y(), r.width(), r.height()); - painter.end(); -} - -void KisPaintDevice::subtractSelection(KisSelectionSP selection) { - KisPainter painter(this->selection().data()); - selection->invert(); - - TQRect r = selection->selectedExactRect(); - painter.bitBlt(r.x(), r.y(), COMPOSITE_ERASE, selection.data(), r.x(), r.y(), r.width(), r.height()); - - selection->invert(); - painter.end(); -} - -void KisPaintDevice::clearSelection() -{ - if (!hasSelection()) return; - - TQRect r = m_selection->selectedExactRect(); - - if (r.isValid()) { - - for (TQ_INT32 y = 0; y < r.height(); y++) { - - KisHLineIterator devIt = createHLineIterator(r.x(), r.y() + y, r.width(), true); - KisHLineIterator selectionIt = m_selection->createHLineIterator(r.x(), r.y() + y, r.width(), false); - - while (!devIt.isDone()) { - // XXX: Optimize by using stretches - - m_colorSpace->applyInverseAlphaU8Mask( devIt.rawData(), selectionIt.rawData(), 1); - - ++devIt; - ++selectionIt; - } - } - - if (m_parentLayer) { - m_parentLayer->setDirty(r); - } - } -} - -void KisPaintDevice::applySelectionMask(KisSelectionSP mask) -{ - TQRect r = mask->selectedRect(); - crop(r); - - for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { - - KisHLineIterator pixelIt = createHLineIterator(r.x(), y, r.width(), true); - KisHLineIterator maskIt = mask->createHLineIterator(r.x(), y, r.width(), false); - - while (!pixelIt.isDone()) { - // XXX: Optimize by using stretches - - m_colorSpace->applyAlphaU8Mask( pixelIt.rawData(), maskIt.rawData(), 1); - - ++pixelIt; - ++maskIt; - } - } -} - -KisSelectionSP KisPaintDevice::setSelection( KisSelectionSP selection) -{ - if (selection) { - KisSelectionSP oldSelection = m_selection; - m_selection = selection; - m_hasSelection = true; - return oldSelection; - } - else return 0; -} - -bool KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y, TQColor *c, TQ_UINT8 *opacity) -{ - KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false); - - TQ_UINT8 *pix = iter.rawData(); - - if (!pix) return false; - - colorSpace()->toTQColor(pix, c, opacity); - - return true; -} - - -bool KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y, KisColor * kc) -{ - KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false); - - TQ_UINT8 *pix = iter.rawData(); - - if (!pix) return false; - - kc->setColor(pix, m_colorSpace); - - return true; -} - -KisColor KisPaintDevice::colorAt(TQ_INT32 x, TQ_INT32 y) -{ - //return KisColor(m_datamanager->pixel(x - m_x, y - m_y), m_colorSpace); - KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); - return KisColor(iter.rawData(), m_colorSpace); -} - -bool KisPaintDevice::setPixel(TQ_INT32 x, TQ_INT32 y, const TQColor& c, TQ_UINT8 opacity) -{ - KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); - - colorSpace()->fromTQColor(c, opacity, iter.rawData()); - - return true; -} - -bool KisPaintDevice::setPixel(TQ_INT32 x, TQ_INT32 y, const KisColor& kc) -{ - TQ_UINT8 * pix; - if (kc.colorSpace() != m_colorSpace) { - KisColor kc2 (kc, m_colorSpace); - pix = kc2.data(); - } - else { - pix = kc.data(); - } - - KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); - memcpy(iter.rawData(), pix, m_colorSpace->pixelSize()); - - return true; -} - - -TQ_INT32 KisPaintDevice::numContiguousColumns(TQ_INT32 x, TQ_INT32 minY, TQ_INT32 maxY) -{ - return m_datamanager->numContiguousColumns(x - m_x, minY - m_y, maxY - m_y); -} - -TQ_INT32 KisPaintDevice::numContiguousRows(TQ_INT32 y, TQ_INT32 minX, TQ_INT32 maxX) -{ - return m_datamanager->numContiguousRows(y - m_y, minX - m_x, maxX - m_x); -} - -TQ_INT32 KisPaintDevice::rowStride(TQ_INT32 x, TQ_INT32 y) -{ - return m_datamanager->rowStride(x - m_x, y - m_y); -} - -const TQ_UINT8* KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y) -{ - return m_datamanager->pixel(x - m_x, y - m_y); -} - -TQ_UINT8* KisPaintDevice::writablePixel(TQ_INT32 x, TQ_INT32 y) -{ - return m_datamanager->writablePixel(x - m_x, y - m_y); -} - -void KisPaintDevice::setX(TQ_INT32 x) -{ - m_x = x; - if(m_selection && m_selection != this) - m_selection->setX(x); -} - -void KisPaintDevice::setY(TQ_INT32 y) -{ - m_y = y; - if(m_selection && m_selection != this) - m_selection->setY(y); -} - - -void KisPaintDevice::readBytes(TQ_UINT8 * data, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - m_datamanager->readBytes(data, x - m_x, y - m_y, w, h); -} - -void KisPaintDevice::writeBytes(const TQ_UINT8 * data, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - m_datamanager->writeBytes( data, x - m_x, y - m_y, w, h); -} - - -KisDataManagerSP KisPaintDevice::dataManager() const -{ - return m_datamanager; -} - -KisExifInfo* KisPaintDevice::exifInfo() -{ - if(!m_exifInfo) - m_exifInfo = new KisExifInfo(); - return m_exifInfo; -} - -void KisPaintDevice::runBackgroundFilters() -{ - if ( m_lock ) return; - - KisTransaction * cmd = new KisTransaction("Running autofilters", this); - - TQRect rc = extent(); - if (!m_longRunningFilters.isEmpty()) { - TQValueList::iterator it; - TQValueList::iterator end = m_longRunningFilters.end(); - for (it = m_longRunningFilters.begin(); it != end; ++it) { - (*it)->process(this, this, 0, rc); - } - } - if (cmd && undoAdapter()) undoAdapter()->addCommand(cmd); - - if (m_parentLayer) m_parentLayer->setDirty(rc); -} - -#include "kis_paint_device.moc" diff --git a/chalk/core/kis_paint_device.cpp b/chalk/core/kis_paint_device.cpp new file mode 100644 index 00000000..da25caec --- /dev/null +++ b/chalk/core/kis_paint_device.cpp @@ -0,0 +1,1285 @@ +/* + * Copyright (c) 2002 Patrick Julien + * Copyright (c) 2004 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "kis_global.h" +#include "kis_types.h" +#include "kis_painter.h" +#include "kis_fill_painter.h" +#include "kis_undo_adapter.h" +#include "kis_iterator.h" +#include "kis_iterators_pixel.h" +#include "kis_iteratorpixeltrait.h" +#include "kis_random_accessor.h" +#include "kis_random_sub_accessor.h" +#include "kis_transaction.h" +#include "kis_profile.h" +#include "kis_color.h" +#include "kis_integer_maths.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_selection.h" +#include "kis_layer.h" +#include "kis_paint_device_iface.h" +#include "kis_paint_device.h" +#include "kis_datamanager.h" +#include "kis_memento.h" +#include "kis_selection.h" + +#include "kis_exif_info.h" + +namespace { + + class KisPaintDeviceCommand : public KNamedCommand { + typedef KNamedCommand super; + + public: + KisPaintDeviceCommand(const TQString& name, KisPaintDeviceSP paintDevice); + virtual ~KisPaintDeviceCommand() {} + + virtual void execute() = 0; + virtual void unexecute() = 0; + + protected: + void setUndo(bool undo); + + KisPaintDeviceSP m_paintDevice; + }; + + KisPaintDeviceCommand::KisPaintDeviceCommand(const TQString& name, KisPaintDeviceSP paintDevice) : + super(name), m_paintDevice(paintDevice) + { + } + + void KisPaintDeviceCommand::setUndo(bool undo) + { + if (m_paintDevice->undoAdapter()) { + m_paintDevice->undoAdapter()->setUndo(undo); + } + } + + class MoveCommand : public KNamedCommand { + typedef KNamedCommand super; + + public: + MoveCommand(KisPaintDeviceSP device, const TQPoint& oldpos, const TQPoint& newpos); + virtual ~MoveCommand(); + + virtual void execute(); + virtual void unexecute(); + + private: + void moveTo(const TQPoint& pos); + void undoOff(); + void undoOn(); + + private: + KisPaintDeviceSP m_device; + TQPoint m_oldPos; + TQPoint m_newPos; + }; + + MoveCommand::MoveCommand(KisPaintDeviceSP device, const TQPoint& oldpos, const TQPoint& newpos) : + super(i18n("Move Layer")) + { + m_device = device; + m_oldPos = oldpos; + m_newPos = newpos; + } + + MoveCommand::~MoveCommand() + { + } + + void MoveCommand::undoOff() + { + if (m_device->undoAdapter()) { + m_device->undoAdapter()->setUndo(false); + } + } + + void MoveCommand::undoOn() + { + if (m_device->undoAdapter()) { + m_device->undoAdapter()->setUndo(true); + } + } + + void MoveCommand::execute() + { + undoOff(); + moveTo(m_newPos); + undoOn(); + } + + void MoveCommand::unexecute() + { + undoOff(); + moveTo(m_oldPos); + undoOn(); + } + + void MoveCommand::moveTo(const TQPoint& pos) + { + m_device->move(pos.x(), pos.y()); + } + + class KisConvertLayerTypeCmd : public KNamedCommand { + typedef KNamedCommand super; + + public: + KisConvertLayerTypeCmd(KisUndoAdapter *adapter, KisPaintDeviceSP paintDevice, + KisDataManagerSP beforeData, KisColorSpace * beforeColorSpace, + KisDataManagerSP afterData, KisColorSpace * afterColorSpace + ) : super(i18n("Convert Layer Type")) + { + m_adapter = adapter; + m_paintDevice = paintDevice; + m_beforeData = beforeData; + m_beforeColorSpace = beforeColorSpace; + m_afterData = afterData; + m_afterColorSpace = afterColorSpace; + } + + virtual ~KisConvertLayerTypeCmd() + { + } + + public: + virtual void execute() + { + m_adapter->setUndo(false); + m_paintDevice->setData(m_afterData, m_afterColorSpace); + m_adapter->setUndo(true); + } + + virtual void unexecute() + { + m_adapter->setUndo(false); + m_paintDevice->setData(m_beforeData, m_beforeColorSpace); + m_adapter->setUndo(true); + } + + private: + KisUndoAdapter *m_adapter; + + KisPaintDeviceSP m_paintDevice; + + KisDataManagerSP m_beforeData; + KisColorSpace * m_beforeColorSpace; + + KisDataManagerSP m_afterData; + KisColorSpace * m_afterColorSpace; + }; + +} + +KisPaintDevice::KisPaintDevice(KisColorSpace * colorSpace, const char * name) : + TQObject(0, name), TDEShared(), m_exifInfo(0), m_lock( false ) +{ + if (colorSpace == 0) { + kdWarning(41001) << "Cannot create paint device without colorstrategy!\n"; + return; + } + m_longRunningFilterTimer = 0; + m_dcop = 0; + + m_x = 0; + m_y = 0; + + m_pixelSize = colorSpace->pixelSize(); + m_nChannels = colorSpace->nChannels(); + + TQ_UINT8* defPixel = new TQ_UINT8 [ m_pixelSize ]; + colorSpace->fromTQColor(TQt::black, OPACITY_TRANSPARENT, defPixel); + + m_datamanager = new KisDataManager(m_pixelSize, defPixel); + delete [] defPixel; + + TQ_CHECK_PTR(m_datamanager); + m_extentIsValid = true; + + m_parentLayer = 0; + + m_colorSpace = colorSpace; + + m_hasSelection = false; + m_selectionDeselected = false; + m_selection = 0; + +} + +KisPaintDevice::KisPaintDevice(KisLayer *parent, KisColorSpace * colorSpace, const char * name) : + TQObject(0, name), TDEShared(), m_exifInfo(0), m_lock( false ) +{ + + m_longRunningFilterTimer = 0; + m_dcop = 0; + + m_x = 0; + m_y = 0; + + m_hasSelection = false; + m_selectionDeselected = false; + m_selection = 0; + + m_parentLayer = parent; + + if (colorSpace == 0 && parent != 0 && parent->image() != 0) { + m_colorSpace = parent->image()->colorSpace(); + } + else { + m_colorSpace = colorSpace; + } + + Q_ASSERT( m_colorSpace ); + + m_pixelSize = m_colorSpace->pixelSize(); + m_nChannels = m_colorSpace->nChannels(); + + TQ_UINT8* defPixel = new TQ_UINT8[ m_pixelSize ]; + m_colorSpace->fromTQColor(TQt::black, OPACITY_TRANSPARENT, defPixel); + + m_datamanager = new KisDataManager(m_pixelSize, defPixel); + delete [] defPixel; + TQ_CHECK_PTR(m_datamanager); + m_extentIsValid = true; + + if ( TQString ( name ) == TQString( "Layer 1" ) ) { + m_longRunningFilters = m_colorSpace->createBackgroundFilters(); + + if (!m_longRunningFilters.isEmpty()) { + m_longRunningFilterTimer = new TQTimer(this); + connect(m_longRunningFilterTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(runBackgroundFilters())); + m_longRunningFilterTimer->start(2000); + } + } +} + + +KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs) : TQObject(), TDEShared(rhs) +{ + if (this != &rhs) { + m_longRunningFilterTimer = 0; + m_parentLayer = 0; + m_dcop = rhs.m_dcop; + if (rhs.m_datamanager) { + m_datamanager = new KisDataManager(*rhs.m_datamanager); + TQ_CHECK_PTR(m_datamanager); + } + else { + kdWarning() << "rhs " << rhs.name() << " has no datamanager\n"; + } + m_extentIsValid = rhs.m_extentIsValid; + m_x = rhs.m_x; + m_y = rhs.m_y; + m_colorSpace = rhs.m_colorSpace; + m_hasSelection = rhs.m_hasSelection; + + if ( m_hasSelection ) + m_selection = new KisSelection(*rhs.m_selection); + else + m_selection = 0; + + m_pixelSize = rhs.m_pixelSize; + m_nChannels = rhs.m_nChannels; + if(rhs.m_exifInfo) + { + m_exifInfo = new KisExifInfo(*rhs.m_exifInfo); + } + else { + m_exifInfo = 0; + } + } +} + +KisPaintDevice::~KisPaintDevice() +{ + delete m_dcop; + delete m_longRunningFilterTimer; + TQValueList::iterator it; + TQValueList::iterator end = m_longRunningFilters.end(); + for (it = m_longRunningFilters.begin(); it != end; ++it) { + KisFilter * f = (*it); + delete f; + } + m_longRunningFilters.clear(); + //delete m_exifInfo; +} + +DCOPObject *KisPaintDevice::dcopObject() +{ + if (!m_dcop) { + m_dcop = new KisPaintDeviceIface(this); + TQ_CHECK_PTR(m_dcop); + } + return m_dcop; +} + +KisLayer *KisPaintDevice::parentLayer() const +{ + return m_parentLayer; +} + +void KisPaintDevice::setParentLayer(KisLayer *parentLayer) +{ + m_parentLayer = parentLayer; +} + +void KisPaintDevice::setDirty(const TQRect & rc) +{ + if (m_parentLayer) m_parentLayer->setDirty(rc); +} + +void KisPaintDevice::setDirty() +{ + if (m_parentLayer) m_parentLayer->setDirty(); +} + +KisImage *KisPaintDevice::image() const +{ + if (m_parentLayer) { + return m_parentLayer->image(); + } else { + return 0; + } +} + + +void KisPaintDevice::move(TQ_INT32 x, TQ_INT32 y) +{ + TQRect dirtyRect = extent(); + + m_x = x; + m_y = y; + + dirtyRect |= extent(); + + if(m_selection) + { + m_selection->setX(x); + m_selection->setY(y); + } + + setDirty(dirtyRect); + + emit positionChanged(this); +} + +void KisPaintDevice::move(const TQPoint& pt) +{ + move(pt.x(), pt.y()); +} + +KNamedCommand * KisPaintDevice::moveCommand(TQ_INT32 x, TQ_INT32 y) +{ + KNamedCommand * cmd = new MoveCommand(this, TQPoint(m_x, m_y), TQPoint(x, y)); + TQ_CHECK_PTR(cmd); + cmd->execute(); + return cmd; +} + +void KisPaintDevice::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const +{ + m_datamanager->extent(x, y, w, h); + x += m_x; + y += m_y; +} + +TQRect KisPaintDevice::extent() const +{ + TQ_INT32 x, y, w, h; + extent(x, y, w, h); + return TQRect(x, y, w, h); +} + +bool KisPaintDevice::extentIsValid() const +{ + return m_extentIsValid; +} + +void KisPaintDevice::setExtentIsValid(bool isValid) +{ + m_extentIsValid = isValid; +} + +void KisPaintDevice::exactBounds(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const +{ + TQRect r = exactBounds(); + x = r.x(); + y = r.y(); + w = r.width(); + h = r.height(); +} + +TQRect KisPaintDevice::exactBoundsOldMethod() const +{ + TQ_INT32 x, y, w, h, boundX, boundY, boundW, boundH; + extent(x, y, w, h); + + extent(boundX, boundY, boundW, boundH); + + const TQ_UINT8* defaultPixel = m_datamanager->defaultPixel(); + + bool found = false; + + for (TQ_INT32 y2 = y; y2 < y + h ; ++y2) { + KisHLineIterator it = const_cast(this)->createHLineIterator(x, y2, w, false); + while (!it.isDone() && found == false) { + if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { + boundY = y2; + found = true; + break; + } + ++it; + } + if (found) break; + } + + found = false; + + for (TQ_INT32 y2 = y + h; y2 > y ; --y2) { + KisHLineIterator it = const_cast(this)->createHLineIterator(x, y2, w, false); + while (!it.isDone() && found == false) { + if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { + boundH = y2 - boundY + 1; + found = true; + break; + } + ++it; + } + if (found) break; + } + found = false; + + for (TQ_INT32 x2 = x; x2 < x + w ; ++x2) { + KisVLineIterator it = const_cast(this)->createVLineIterator(x2, y, h, false); + while (!it.isDone() && found == false) { + if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { + boundX = x2; + found = true; + break; + } + ++it; + } + if (found) break; + } + + found = false; + + // Look for right edge ) + for (TQ_INT32 x2 = x + w; x2 > x ; --x2) { + KisVLineIterator it = const_cast(this)->createVLineIterator(x2, y, h, false); + while (!it.isDone() && found == false) { + if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { + boundW = x2 - boundX + 1; // XXX: I commented this + // +1 out, but why? It + // should be correct, since + // we've found the first + // pixel that should be + // included, and it should + // be added to the width. + found = true; + break; + } + ++it; + } + if (found) break; + } + + return TQRect(boundX, boundY, boundW, boundH); +} + +TQRect KisPaintDevice::exactBoundsImprovedOldMethod() const +{ + // Solution n°2 + TQ_INT32 x, y, w, h, boundX2, boundY2, boundW2, boundH2; + extent(x, y, w, h); + extent(boundX2, boundY2, boundW2, boundH2); + + const TQ_UINT8* defaultPixel = m_datamanager->defaultPixel(); + bool found = false; + { + KisHLineIterator it = const_cast(this)->createHLineIterator(x, y, w, false); + for (TQ_INT32 y2 = y; y2 < y + h ; ++y2) { + while (!it.isDone() && found == false) { + if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { + boundY2 = y2; + found = true; + break; + } + ++it; + } + if (found) break; + it.nextRow(); + } + } + + found = false; + + for (TQ_INT32 y2 = y + h; y2 > y ; --y2) { + KisHLineIterator it = const_cast(this)->createHLineIterator(x, y2, w, false); + while (!it.isDone() && found == false) { + if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { + boundH2 = y2 - boundY2 + 1; + found = true; + break; + } + ++it; + } + if (found) break; + } + found = false; + + { + KisVLineIterator it = const_cast(this)->createVLineIterator(x, boundY2, boundH2, false); + for (TQ_INT32 x2 = x; x2 < x + w ; ++x2) { + while (!it.isDone() && found == false) { + if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { + boundX2 = x2; + found = true; + break; + } + ++it; + } + if (found) break; + it.nextCol(); + } + } + + found = false; + + // Look for right edge ) + { + for (TQ_INT32 x2 = x + w; x2 > x ; --x2) { + KisVLineIterator it = const_cast(this)->createVLineIterator(/*x + w*/ x2, boundY2, boundH2, false); + while (!it.isDone() && found == false) { + if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) { + boundW2 = x2 - boundX2 + 1; // XXX: I commented this + // +1 out, but why? It + // should be correct, since + // we've found the first + // pixel that should be + // included, and it should + // be added to the width. + found = true; + break; + } + ++it; + } + if (found) break; + } + } + return TQRect(boundX2, boundY2, boundW2, boundH2); +} + + +TQRect KisPaintDevice::exactBounds() const +{ + TQRect r2 = exactBoundsImprovedOldMethod(); + return r2; +} + +void KisPaintDevice::crop(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + m_datamanager->setExtent(x - m_x, y - m_y, w, h); +} + + +void KisPaintDevice::crop(TQRect r) +{ + r.moveBy(-m_x, -m_y); m_datamanager->setExtent(r); +} + +void KisPaintDevice::clear() +{ + m_datamanager->clear(); +} + +void KisPaintDevice::fill(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const TQ_UINT8 *fillPixel) +{ + m_datamanager->clear(x, y, w, h, fillPixel); +} + +void KisPaintDevice::mirrorX() +{ + TQRect r; + if (hasSelection()) { + r = selection()->selectedRect(); + } + else { + r = exactBounds(); + } + + for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { + KisHLineIteratorPixel srcIt = createHLineIterator(r.x(), y, r.width(), false); + KisHLineIteratorPixel dstIt = createHLineIterator(r.x(), y, r.width(), true); + + dstIt += r.width() - 1; + + while (!srcIt.isDone()) { + if (srcIt.isSelected()) { + memcpy(dstIt.rawData(), srcIt.oldRawData(), m_pixelSize); + } + ++srcIt; + --dstIt; + + } + } + if (m_parentLayer) { + m_parentLayer->setDirty(r); + } +} + +void KisPaintDevice::mirrorY() +{ + /* Read a line from bottom to top and and from top to bottom and write their values to each other */ + TQRect r; + if (hasSelection()) { + r = selection()->selectedRect(); + } + else { + r = exactBounds(); + } + + + TQ_INT32 y1, y2; + for (y1 = r.top(), y2 = r.bottom(); y1 <= r.bottom(); ++y1, --y2) { + KisHLineIteratorPixel itTop = createHLineIterator(r.x(), y1, r.width(), true); + KisHLineIteratorPixel itBottom = createHLineIterator(r.x(), y2, r.width(), false); + while (!itTop.isDone() && !itBottom.isDone()) { + if (itBottom.isSelected()) { + memcpy(itTop.rawData(), itBottom.oldRawData(), m_pixelSize); + } + ++itBottom; + ++itTop; + } + } + + if (m_parentLayer) { + m_parentLayer->setDirty(r); + } +} + +KisMementoSP KisPaintDevice::getMemento() +{ + return m_datamanager->getMemento(); +} + +void KisPaintDevice::rollback(KisMementoSP memento) { m_datamanager->rollback(memento); } + +void KisPaintDevice::rollforward(KisMementoSP memento) { m_datamanager->rollforward(memento); } + +bool KisPaintDevice::write(KoStore *store) +{ + bool retval = m_datamanager->write(store); + emit ioProgress(100); + + return retval; +} + +bool KisPaintDevice::read(KoStore *store) +{ + bool retval = m_datamanager->read(store); + emit ioProgress(100); + + return retval; +} + +void KisPaintDevice::convertTo(KisColorSpace * dstColorSpace, TQ_INT32 renderingIntent) +{ + kdDebug(41004) << "Converting " << name() << " to " << dstColorSpace->id().id() << " from " + << m_colorSpace->id().id() << "\n"; + if ( colorSpace() == dstColorSpace ) + { + return; + } + + KisPaintDevice dst(dstColorSpace); + dst.setX(getX()); + dst.setY(getY()); + + TQ_INT32 x, y, w, h; + extent(x, y, w, h); + + for (TQ_INT32 row = y; row < y + h; ++row) { + + TQ_INT32 column = x; + TQ_INT32 columnsRemaining = w; + + while (columnsRemaining > 0) { + + TQ_INT32 numContiguousDstColumns = dst.numContiguousColumns(column, row, row); + TQ_INT32 numContiguousSrcColumns = numContiguousColumns(column, row, row); + + TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns); + columns = TQMIN(columns, columnsRemaining); + + //const TQ_UINT8 *srcData = pixel(column, row); + //TQ_UINT8 *dstData = dst.writablePixel(column, row); + KisHLineIteratorPixel srcIt = createHLineIterator(column, row, columns, false); + KisHLineIteratorPixel dstIt = dst.createHLineIterator(column, row, columns, true); + + const TQ_UINT8 *srcData = srcIt.rawData(); + TQ_UINT8 *dstData = dstIt.rawData(); + + + m_colorSpace->convertPixelsTo(srcData, dstData, dstColorSpace, columns, renderingIntent); + + column += columns; + columnsRemaining -= columns; + } + } + + KisDataManagerSP oldData = m_datamanager; + KisColorSpace *oldColorSpace = m_colorSpace; + + setData(dst.m_datamanager, dstColorSpace); + + if (undoAdapter() && undoAdapter()->undo()) { + undoAdapter()->addCommand(new KisConvertLayerTypeCmd(undoAdapter(), this, oldData, oldColorSpace, m_datamanager, m_colorSpace)); + } +} + +void KisPaintDevice::setProfile(KisProfile * profile) +{ + if (profile == 0) return; + + KisColorSpace * dstSpace = + KisMetaRegistry::instance()->csRegistry()->getColorSpace( colorSpace()->id(), + profile); + if (dstSpace) + m_colorSpace = dstSpace; + +} + +void KisPaintDevice::setData(KisDataManagerSP data, KisColorSpace * colorSpace) +{ + m_datamanager = data; + m_colorSpace = colorSpace; + m_pixelSize = m_colorSpace->pixelSize(); + m_nChannels = m_colorSpace->nChannels(); + + if (m_parentLayer) { + m_parentLayer->setDirty(extent()); + m_parentLayer->notifyPropertyChanged(); + } +} + +KisUndoAdapter *KisPaintDevice::undoAdapter() const +{ + if (m_parentLayer && m_parentLayer->image()) { + return m_parentLayer->image()->undoAdapter(); + } + return 0; +} + +void KisPaintDevice::convertFromTQImage(const TQImage& image, const TQString &srcProfileName, + TQ_INT32 offsetX, TQ_INT32 offsetY) +{ + TQImage img = image; + + // Chalk is little-endian inside. + if (img.bitOrder() == TQImage::LittleEndian) { + img = img.convertBitOrder(TQImage::BigEndian); + } + kdDebug() << k_funcinfo << img.bitOrder()<< endl; + // Chalk likes bgra (convertDepth returns *this is the img is alread 32 bits) + img = img.convertDepth( 32 ); +#if 0 + // XXX: Apply import profile + if (colorSpace() == KisMetaRegistry::instance()->csRegistry() ->getColorSpace(KisID("RGBA",""),"")) { + writeBytes(img.bits(), 0, 0, img.width(), img.height()); + } + else { +#endif + TQ_UINT8 * dstData = new TQ_UINT8[img.width() * img.height() * pixelSize()]; + KisMetaRegistry::instance()->csRegistry() + ->getColorSpace(KisID("RGBA",""),srcProfileName)-> + convertPixelsTo(img.bits(), dstData, colorSpace(), img.width() * img.height()); + writeBytes(dstData, offsetX, offsetY, img.width(), img.height()); +// } +} + +TQImage KisPaintDevice::convertToTQImage(KisProfile * dstProfile, float exposure) +{ + TQ_INT32 x1; + TQ_INT32 y1; + TQ_INT32 w; + TQ_INT32 h; + + x1 = - getX(); + y1 = - getY(); + + if (image()) { + w = image()->width(); + h = image()->height(); + } + else { + extent(x1, y1, w, h); + } + + return convertToTQImage(dstProfile, x1, y1, w, h, exposure); +} + +// XXX: is this faster than building the TQImage ourselves? It makes +TQImage KisPaintDevice::convertToTQImage(KisProfile * dstProfile, TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, float exposure) +{ + if (w < 0) + return TQImage(); + + if (h < 0) + return TQImage(); + + TQ_UINT8 * data = new TQ_UINT8 [w * h * m_pixelSize]; + TQ_CHECK_PTR(data); + + // XXX: Is this really faster than converting line by line and building the TQImage directly? + // This copies potentially a lot of data. + readBytes(data, x1, y1, w, h); + TQImage image = colorSpace()->convertToTQImage(data, w, h, dstProfile, INTENT_PERCEPTUAL, exposure); + delete[] data; + + return image; +} + +KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(TQ_INT32 w, TQ_INT32 h) +{ + KisPaintDeviceSP thumbnail = new KisPaintDevice(colorSpace(), "thumbnail"); + + thumbnail->clear(); + + int srcw, srch; + if( image() ) + { + srcw = image()->width(); + srch = image()->height(); + } + else + { + const TQRect e = exactBounds(); + srcw = e.width(); + srch = e.height(); + } + + if (w > srcw) + { + w = srcw; + h = TQ_INT32(double(srcw) / w * h); + } + if (h > srch) + { + h = srch; + w = TQ_INT32(double(srch) / h * w); + } + + if (srcw > srch) + h = TQ_INT32(double(srch) / srcw * w); + else if (srch > srcw) + w = TQ_INT32(double(srcw) / srch * h); + + for (TQ_INT32 y=0; y < h; ++y) { + TQ_INT32 iY = (y * srch ) / h; + for (TQ_INT32 x=0; x < w; ++x) { + TQ_INT32 iX = (x * srcw ) / w; + thumbnail->setPixel(x, y, colorAt(iX, iY)); + } + } + + return thumbnail; + +} + + +TQImage KisPaintDevice::createThumbnail(TQ_INT32 w, TQ_INT32 h) +{ + int srcw, srch; + if( image() ) + { + srcw = image()->width(); + srch = image()->height(); + } + else + { + const TQRect e = extent(); + srcw = e.width(); + srch = e.height(); + } + + if (w > srcw) + { + w = srcw; + h = TQ_INT32(double(srcw) / w * h); + } + if (h > srch) + { + h = srch; + w = TQ_INT32(double(srch) / h * w); + } + + if (srcw > srch) + h = TQ_INT32(double(srch) / srcw * w); + else if (srch > srcw) + w = TQ_INT32(double(srcw) / srch * h); + + TQColor c; + TQ_UINT8 opacity; + TQImage img(w,h,32); + + for (TQ_INT32 y=0; y < h; ++y) { + TQ_INT32 iY = (y * srch ) / h; + for (TQ_INT32 x=0; x < w; ++x) { + TQ_INT32 iX = (x * srcw ) / w; + pixel(iX, iY, &c, &opacity); + const TQRgb rgb = c.rgb(); + img.setPixel(x, y, tqRgba(tqRed(rgb), tqGreen(rgb), tqBlue(rgb), opacity)); + } + } + + return img; +} + +KisRectIteratorPixel KisPaintDevice::createRectIterator(TQ_INT32 left, TQ_INT32 top, TQ_INT32 w, TQ_INT32 h, bool writable) +{ + if(hasSelection()) + return KisRectIteratorPixel(this, m_datamanager, m_selection->m_datamanager, left, top, w, h, m_x, m_y, writable); + else + return KisRectIteratorPixel(this, m_datamanager, NULL, left, top, w, h, m_x, m_y, writable); +} + +KisHLineIteratorPixel KisPaintDevice::createHLineIterator(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable) +{ + if(hasSelection()) + return KisHLineIteratorPixel(this, m_datamanager, m_selection->m_datamanager, x, y, w, m_x, m_y, writable); + else + return KisHLineIteratorPixel(this, m_datamanager, NULL, x, y, w, m_x, m_y, writable); +} + +KisVLineIteratorPixel KisPaintDevice::createVLineIterator(TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable) +{ + if(hasSelection()) + return KisVLineIteratorPixel(this, m_datamanager, m_selection->m_datamanager, x, y, h, m_x, m_y, writable); + else + return KisVLineIteratorPixel(this, m_datamanager, NULL, x, y, h, m_x, m_y, writable); + +} + +KisRandomAccessorPixel KisPaintDevice::createRandomAccessor(TQ_INT32 x, TQ_INT32 y, bool writable) { + if(hasSelection()) + return KisRandomAccessorPixel(m_datamanager, m_selection->m_datamanager, x, y, m_x, m_y, writable); + else + return KisRandomAccessorPixel(m_datamanager, NULL, x, y, m_x, m_y, writable); +} + +KisRandomSubAccessorPixel KisPaintDevice::createRandomSubAccessor() +{ + return KisRandomSubAccessorPixel(this); +} + +void KisPaintDevice::emitSelectionChanged() +{ + if (m_parentLayer && m_parentLayer->image()) { + m_parentLayer->image()->slotSelectionChanged(); + } +} + +void KisPaintDevice::emitSelectionChanged(const TQRect& r) +{ + if (m_parentLayer && m_parentLayer->image()) { + m_parentLayer->image()->slotSelectionChanged(r); + } +} + +KisSelectionSP KisPaintDevice::selection() +{ + if ( m_selectionDeselected && m_selection ) { + m_selectionDeselected = false; + } + else if (!m_selection) { + m_selection = new KisSelection(this); + TQ_CHECK_PTR(m_selection); + m_selection->setX(m_x); + m_selection->setY(m_y); + } + m_hasSelection = true; + + return m_selection; +} + + +bool KisPaintDevice::hasSelection() +{ + return m_hasSelection; +} + +bool KisPaintDevice::selectionDeselected() +{ + return m_selectionDeselected; +} + + +void KisPaintDevice::deselect() +{ + if (m_selection && m_hasSelection) { + m_hasSelection = false; + m_selectionDeselected = true; + } +} + +void KisPaintDevice::reselect() +{ + m_hasSelection = true; + m_selectionDeselected = false; +} + +void KisPaintDevice::addSelection(KisSelectionSP selection) { + + KisPainter painter(this->selection().data()); + TQRect r = selection->selectedExactRect(); + painter.bitBlt(r.x(), r.y(), COMPOSITE_OVER, selection.data(), r.x(), r.y(), r.width(), r.height()); + painter.end(); +} + +void KisPaintDevice::subtractSelection(KisSelectionSP selection) { + KisPainter painter(this->selection().data()); + selection->invert(); + + TQRect r = selection->selectedExactRect(); + painter.bitBlt(r.x(), r.y(), COMPOSITE_ERASE, selection.data(), r.x(), r.y(), r.width(), r.height()); + + selection->invert(); + painter.end(); +} + +void KisPaintDevice::clearSelection() +{ + if (!hasSelection()) return; + + TQRect r = m_selection->selectedExactRect(); + + if (r.isValid()) { + + for (TQ_INT32 y = 0; y < r.height(); y++) { + + KisHLineIterator devIt = createHLineIterator(r.x(), r.y() + y, r.width(), true); + KisHLineIterator selectionIt = m_selection->createHLineIterator(r.x(), r.y() + y, r.width(), false); + + while (!devIt.isDone()) { + // XXX: Optimize by using stretches + + m_colorSpace->applyInverseAlphaU8Mask( devIt.rawData(), selectionIt.rawData(), 1); + + ++devIt; + ++selectionIt; + } + } + + if (m_parentLayer) { + m_parentLayer->setDirty(r); + } + } +} + +void KisPaintDevice::applySelectionMask(KisSelectionSP mask) +{ + TQRect r = mask->selectedRect(); + crop(r); + + for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { + + KisHLineIterator pixelIt = createHLineIterator(r.x(), y, r.width(), true); + KisHLineIterator maskIt = mask->createHLineIterator(r.x(), y, r.width(), false); + + while (!pixelIt.isDone()) { + // XXX: Optimize by using stretches + + m_colorSpace->applyAlphaU8Mask( pixelIt.rawData(), maskIt.rawData(), 1); + + ++pixelIt; + ++maskIt; + } + } +} + +KisSelectionSP KisPaintDevice::setSelection( KisSelectionSP selection) +{ + if (selection) { + KisSelectionSP oldSelection = m_selection; + m_selection = selection; + m_hasSelection = true; + return oldSelection; + } + else return 0; +} + +bool KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y, TQColor *c, TQ_UINT8 *opacity) +{ + KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false); + + TQ_UINT8 *pix = iter.rawData(); + + if (!pix) return false; + + colorSpace()->toTQColor(pix, c, opacity); + + return true; +} + + +bool KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y, KisColor * kc) +{ + KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false); + + TQ_UINT8 *pix = iter.rawData(); + + if (!pix) return false; + + kc->setColor(pix, m_colorSpace); + + return true; +} + +KisColor KisPaintDevice::colorAt(TQ_INT32 x, TQ_INT32 y) +{ + //return KisColor(m_datamanager->pixel(x - m_x, y - m_y), m_colorSpace); + KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); + return KisColor(iter.rawData(), m_colorSpace); +} + +bool KisPaintDevice::setPixel(TQ_INT32 x, TQ_INT32 y, const TQColor& c, TQ_UINT8 opacity) +{ + KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); + + colorSpace()->fromTQColor(c, opacity, iter.rawData()); + + return true; +} + +bool KisPaintDevice::setPixel(TQ_INT32 x, TQ_INT32 y, const KisColor& kc) +{ + TQ_UINT8 * pix; + if (kc.colorSpace() != m_colorSpace) { + KisColor kc2 (kc, m_colorSpace); + pix = kc2.data(); + } + else { + pix = kc.data(); + } + + KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); + memcpy(iter.rawData(), pix, m_colorSpace->pixelSize()); + + return true; +} + + +TQ_INT32 KisPaintDevice::numContiguousColumns(TQ_INT32 x, TQ_INT32 minY, TQ_INT32 maxY) +{ + return m_datamanager->numContiguousColumns(x - m_x, minY - m_y, maxY - m_y); +} + +TQ_INT32 KisPaintDevice::numContiguousRows(TQ_INT32 y, TQ_INT32 minX, TQ_INT32 maxX) +{ + return m_datamanager->numContiguousRows(y - m_y, minX - m_x, maxX - m_x); +} + +TQ_INT32 KisPaintDevice::rowStride(TQ_INT32 x, TQ_INT32 y) +{ + return m_datamanager->rowStride(x - m_x, y - m_y); +} + +const TQ_UINT8* KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y) +{ + return m_datamanager->pixel(x - m_x, y - m_y); +} + +TQ_UINT8* KisPaintDevice::writablePixel(TQ_INT32 x, TQ_INT32 y) +{ + return m_datamanager->writablePixel(x - m_x, y - m_y); +} + +void KisPaintDevice::setX(TQ_INT32 x) +{ + m_x = x; + if(m_selection && m_selection != this) + m_selection->setX(x); +} + +void KisPaintDevice::setY(TQ_INT32 y) +{ + m_y = y; + if(m_selection && m_selection != this) + m_selection->setY(y); +} + + +void KisPaintDevice::readBytes(TQ_UINT8 * data, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + m_datamanager->readBytes(data, x - m_x, y - m_y, w, h); +} + +void KisPaintDevice::writeBytes(const TQ_UINT8 * data, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + m_datamanager->writeBytes( data, x - m_x, y - m_y, w, h); +} + + +KisDataManagerSP KisPaintDevice::dataManager() const +{ + return m_datamanager; +} + +KisExifInfo* KisPaintDevice::exifInfo() +{ + if(!m_exifInfo) + m_exifInfo = new KisExifInfo(); + return m_exifInfo; +} + +void KisPaintDevice::runBackgroundFilters() +{ + if ( m_lock ) return; + + KisTransaction * cmd = new KisTransaction("Running autofilters", this); + + TQRect rc = extent(); + if (!m_longRunningFilters.isEmpty()) { + TQValueList::iterator it; + TQValueList::iterator end = m_longRunningFilters.end(); + for (it = m_longRunningFilters.begin(); it != end; ++it) { + (*it)->process(this, this, 0, rc); + } + } + if (cmd && undoAdapter()) undoAdapter()->addCommand(cmd); + + if (m_parentLayer) m_parentLayer->setDirty(rc); +} + +#include "kis_paint_device.moc" diff --git a/chalk/core/kis_paint_device_iface.cc b/chalk/core/kis_paint_device_iface.cc deleted file mode 100644 index c8afa80a..00000000 --- a/chalk/core/kis_paint_device_iface.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* - * This file is part of the KDE project - * - * Copyright (C) 2005 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -#include - -#include - -#include "kis_paint_device_iface.h" -#include "kis_colorspace_iface.h" -#include "kis_colorspace.h" - -#include "kis_paint_device.h" - -KisPaintDeviceIface::KisPaintDeviceIface( KisPaintDevice * parent ) - : DCOPObject("paintdevice") -{ - m_parent = parent; -} - -TQ_INT32 KisPaintDeviceIface::pixelSize() const -{ - return m_parent->pixelSize(); -} - -TQ_INT32 KisPaintDeviceIface::nChannels() const -{ - return m_parent->nChannels(); -} - -TQByteArray KisPaintDeviceIface::readBytes(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - TQByteArray b (w * h * m_parent->pixelSize()); - - m_parent->readBytes((TQ_UINT8*)b.data(), x, y, w, h); - return b; -} - -void KisPaintDeviceIface::writeBytes(TQByteArray bytes, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - m_parent->writeBytes((TQ_UINT8*)bytes.data(), x, y, w, h); -} - -DCOPRef KisPaintDeviceIface::colorSpace() const -{ - KisColorSpace * cs = m_parent->colorSpace(); - if ( !cs ) - return DCOPRef(); - else - return DCOPRef( kapp->dcopClient()->appId(), - cs->dcopObject()->objId(), - "KisColorSpaceIface" ); -} - -void KisPaintDeviceIface::setColorSpace(DCOPRef) -{ - // XXX: Figure out how to get the correct object from - // the dcopref -} diff --git a/chalk/core/kis_paint_device_iface.cpp b/chalk/core/kis_paint_device_iface.cpp new file mode 100644 index 00000000..c8afa80a --- /dev/null +++ b/chalk/core/kis_paint_device_iface.cpp @@ -0,0 +1,74 @@ +/* + * This file is part of the KDE project + * + * Copyright (C) 2005 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include + +#include + +#include "kis_paint_device_iface.h" +#include "kis_colorspace_iface.h" +#include "kis_colorspace.h" + +#include "kis_paint_device.h" + +KisPaintDeviceIface::KisPaintDeviceIface( KisPaintDevice * parent ) + : DCOPObject("paintdevice") +{ + m_parent = parent; +} + +TQ_INT32 KisPaintDeviceIface::pixelSize() const +{ + return m_parent->pixelSize(); +} + +TQ_INT32 KisPaintDeviceIface::nChannels() const +{ + return m_parent->nChannels(); +} + +TQByteArray KisPaintDeviceIface::readBytes(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + TQByteArray b (w * h * m_parent->pixelSize()); + + m_parent->readBytes((TQ_UINT8*)b.data(), x, y, w, h); + return b; +} + +void KisPaintDeviceIface::writeBytes(TQByteArray bytes, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + m_parent->writeBytes((TQ_UINT8*)bytes.data(), x, y, w, h); +} + +DCOPRef KisPaintDeviceIface::colorSpace() const +{ + KisColorSpace * cs = m_parent->colorSpace(); + if ( !cs ) + return DCOPRef(); + else + return DCOPRef( kapp->dcopClient()->appId(), + cs->dcopObject()->objId(), + "KisColorSpaceIface" ); +} + +void KisPaintDeviceIface::setColorSpace(DCOPRef) +{ + // XXX: Figure out how to get the correct object from + // the dcopref +} diff --git a/chalk/core/kis_paint_layer.cc b/chalk/core/kis_paint_layer.cc deleted file mode 100644 index a4c5412d..00000000 --- a/chalk/core/kis_paint_layer.cc +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (c) 2005 Casper Boemann - * Copyright (c) 2006 Bart Coppens - * - * this program is free software; you can redistribute it and/or modify - * it under the terms of the gnu general public license as published by - * the free software foundation; either version 2 of the license, or - * (at your option) any later version. - * - * this program is distributed in the hope that it will be useful, - * but without any warranty; without even the implied warranty of - * merchantability or fitness for a particular purpose. see the - * gnu general public license for more details. - * - * you should have received a copy of the gnu general public license - * along with this program; if not, write to the free software - * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. - */ - -#include -#include - -#include "kis_debug_areas.h" -#include "kis_image.h" -#include "kis_paint_layer.h" -#include "kis_selection.h" -#include "kis_painter.h" -#include "kis_undo_adapter.h" -#include "kis_iterators_pixel.h" -#include "kis_paint_device.h" -#include "kis_meta_registry.h" -#include "kis_colorspace_factory_registry.h" -#include "kis_datamanager.h" -#include "kis_undo_adapter.h" - -KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity, KisPaintDeviceSP dev) - : super(img, name, opacity) -{ - Q_ASSERT(img); - Q_ASSERT(dev); - m_paintdev = dev; - m_mask = 0; - m_maskAsSelection = 0; - m_paintdev->setParentLayer(this); - m_renderMask = false; - m_editMask = true; -} - - -KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity) - : super(img, name, opacity) -{ - Q_ASSERT(img); - m_paintdev = new KisPaintDevice(this, img->colorSpace(), name.latin1()); - m_mask = 0; - m_maskAsSelection = 0; - m_renderMask = false; - m_editMask = true; -} - -KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity, KisColorSpace * colorSpace) - : super(img, name, opacity) -{ - Q_ASSERT(img); - Q_ASSERT(colorSpace); - m_paintdev = new KisPaintDevice(this, colorSpace, name.latin1()); - m_mask = 0; - m_maskAsSelection = 0; - m_renderMask = false; - m_editMask = true; -} - -KisPaintLayer::KisPaintLayer(const KisPaintLayer& rhs) : - KisLayer(rhs), KisLayerSupportsIndirectPainting(rhs) -{ - m_paintdev = new KisPaintDevice( *rhs.m_paintdev.data() ); - m_paintdev->setParentLayer(this); - if (rhs.hasMask()) { - m_mask = new KisPaintDevice(*rhs.m_mask.data()); - m_mask->setParentLayer(this); - } - m_renderMask = rhs.m_renderMask; - m_editMask = rhs.m_editMask; -} - -KisLayerSP KisPaintLayer::clone() const -{ - return new KisPaintLayer(*this); -} - -KisPaintLayer::~KisPaintLayer() -{ - if (m_paintdev != 0) { - m_paintdev->setParentLayer(0); - } - if (m_mask != 0) { - m_mask->setParentLayer(0); - } -} - -void KisPaintLayer::paintSelection(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - if (m_paintdev && m_paintdev->hasSelection()) { - m_paintdev->selection()->paintSelection(img, x, y, w, h); - } else if (m_mask && m_editMask && m_mask->hasSelection()) { - m_mask->selection()->paintSelection(img, x, y, w, h); - } -} - -void KisPaintLayer::paintSelection(TQImage &img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize) -{ - if (m_paintdev && m_paintdev->hasSelection()) { - m_paintdev->selection()->paintSelection(img, scaledImageRect, scaledImageSize, imageSize); - } else if (m_mask && m_editMask && m_mask->hasSelection()) { - m_mask->selection()->paintSelection(img, scaledImageRect, scaledImageSize, imageSize); - } -} - -void KisPaintLayer::paintMaskInactiveLayers(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - uchar *j = img.bits(); - - KisColorSpace *cs = m_paintdev->colorSpace(); - - for (TQ_INT32 y2 = y; y2 < h + y; ++y2) { - KisHLineIteratorPixel it = m_paintdev->createHLineIterator(x, y2, w, false); - while ( ! it.isDone()) { - TQ_UINT8 s = cs->getAlpha(it.rawData()); - if(s==0) - { - TQ_UINT8 g = (*(j + 0) + *(j + 1 ) + *(j + 2 )) / 9; - - *(j+0) = 128+g ; - *(j+1) = 165+g; - *(j+2) = 128+g; - } - j+=4; - ++it; - } - } -} - -TQImage KisPaintLayer::createThumbnail(TQ_INT32 w, TQ_INT32 h) -{ - if (m_paintdev) - return m_paintdev->createThumbnail(w, h); - else - return TQImage(); -} - - -TQ_INT32 KisPaintLayer::x() const { - if (m_paintdev) - return m_paintdev->getX(); - else return 0; -} - -void KisPaintLayer::setX(TQ_INT32 x) -{ - if (m_paintdev) - m_paintdev->setX(x); -} - -TQ_INT32 KisPaintLayer::y() const { - if (m_paintdev) - return m_paintdev->getY(); - else - return 0; -} - -void KisPaintLayer::setY(TQ_INT32 y) { - if (m_paintdev) - m_paintdev->setY(y); -} - -TQRect KisPaintLayer::extent() const { - if (m_paintdev) - return m_paintdev->extent(); - else - return TQRect(); -} - -TQRect KisPaintLayer::exactBounds() const { - if (m_paintdev) - return m_paintdev->exactBounds(); - else - return TQRect(); -} - -void KisPaintLayer::removeMask() { - if (!hasMask()) - return; - - m_mask->setParentLayer(0); - m_mask = 0; - m_maskAsSelection = 0; - setDirty(); - - emit sigMaskInfoChanged(); -} - -// ### XXX Do we apply the mask outside the image boundaries too? I'd say no, but I'm not sure -void KisPaintLayer::applyMask() { - if (!hasMask()) - return; - - int x, y, w, h; - m_paintdev->extent(x, y, w, h); - - // A bit slow; but it works - KisPaintDeviceSP temp = new KisPaintDevice(m_paintdev->colorSpace()); - KisPainter gc(temp); - gc.bltSelection(x, y, COMPOSITE_OVER, m_paintdev, m_maskAsSelection, OPACITY_OPAQUE, x, y, w, h); - gc.end(); - gc.begin(m_paintdev); - gc.bitBlt(x, y, COMPOSITE_COPY, temp, OPACITY_OPAQUE, x, y, w, h); - gc.end(); - - removeMask(); -} - -KisPaintDeviceSP KisPaintLayer::createMask() { - if (hasMask()) - return m_mask; - - kdDebug() << k_funcinfo << endl; - // Grey8 nicely fits our needs of being intuitively comparable to other apps' - // mask layer interfaces. It does have an alpha component though, which is a bit - // less appropriate in this context. - m_mask = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry() - ->getColorSpace(KisID("GRAYA"), 0)); - - genericMaskCreationHelper(); - - return m_mask; -} - -// FIXME If from is a paint device is not grey8!! -void KisPaintLayer::createMaskFromPaintDevice(KisPaintDeviceSP from) { - if (hasMask()) - return; // Or overwrite? XXX - - kdDebug() << k_funcinfo << endl; - m_mask = from; // KisPaintDevice(*from); XXX - - genericMaskCreationHelper(); -} - -void KisPaintLayer::createMaskFromSelection(KisSelectionSP from) { - kdDebug() << k_funcinfo << endl; - m_mask = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry() - ->getColorSpace(KisID("GRAYA"), 0)); - m_mask->setParentLayer(this); - - m_maskAsSelection = new KisSelection(); // Anonymous selection is good enough - - // Default pixel is opaque white == don't mask? - TQ_UINT8 const defPixel[] = { 255, 255 }; - m_mask->dataManager()->setDefaultPixel(defPixel); - - if (from) { - TQRect r(extent()); - - int w = r.width(); - int h = r.height(); - for (int y = r.y(); y < h; y++) { - KisHLineIteratorPixel srcIt = from->createHLineIterator(r.x(), y, w, false); - KisHLineIteratorPixel dstIt = m_mask->createHLineIterator(r.x(), y, w, true); - - while(!dstIt.isDone()) { - // XXX same remark as in convertMaskToSelection - *dstIt.rawData() = *srcIt.rawData(); - ++srcIt; - ++dstIt; - } - } - } - - convertMaskToSelection(extent()); - m_paintdev->deselect(); - - setDirty(); - emit sigMaskInfoChanged(); -} - -KisPaintDeviceSP KisPaintLayer::getMask() { - createMask(); - kdDebug() << k_funcinfo << endl; - return m_mask; -} - -KisSelectionSP KisPaintLayer::getMaskAsSelection() { - createMask(); - kdDebug() << k_funcinfo << endl; - return m_maskAsSelection; -} - -void KisPaintLayer::setEditMask(bool b) { - m_editMask = b; - emit sigMaskInfoChanged(); -} - -void KisPaintLayer::setRenderMask(bool b) { - m_renderMask = b; - - if (hasMask()) - setDirty(); - - emit sigMaskInfoChanged(); -} - -void KisPaintLayer::convertMaskToSelection(const TQRect& r) { - KisRectIteratorPixel srcIt = m_mask->createRectIterator(r.x(), r.y(), - r.width(), r.height(), false); - KisRectIteratorPixel dstIt = m_maskAsSelection->createRectIterator(r.x(), r.y(), - r.width(), r.height(), true); - - while(!dstIt.isDone()) { - // src is grey8 (grey + alpha), dst is alpha8. We convert the grey value to - // alpha8 manually and ignore the alpha (that's why we don't convert using default - // functions, and interpret the data raw!) [ XXX ] - *dstIt.rawData() = *srcIt.rawData(); - ++srcIt; - ++dstIt; - } -} - -void KisPaintLayer::genericMaskCreationHelper() { - m_mask->setParentLayer(this); - - m_maskAsSelection = new KisSelection(); // Anonymous selection is good enough - - // Default pixel is opaque white == don't mask? - TQ_UINT8 const defPixel[] = { 255, 255 }; - m_mask->dataManager()->setDefaultPixel(defPixel); - - setDirty(); - emit sigMaskInfoChanged(); -} - -void KisPaintLayer::setDirty(bool propagate) { - if (hasMask()) - convertMaskToSelection(extent()); - super::setDirty(propagate); -} - -void KisPaintLayer::setDirty(const TQRect & rect, bool propagate) { - if (hasMask()) - convertMaskToSelection(rect); - super::setDirty(rect, propagate); -} - -// Undoable versions code -namespace { - class KisCreateMaskCommand : public KNamedCommand { - typedef KNamedCommand super; - KisPaintLayerSP m_layer; - KisPaintDeviceSP m_mask; - public: - KisCreateMaskCommand(const TQString& name, KisPaintLayer* layer) - : super(name), m_layer(layer) {} - virtual void execute() { - kdDebug() << k_funcinfo << endl; - if (!m_mask) - m_mask = m_layer->createMask(); - else - m_layer->createMaskFromPaintDevice(m_mask); - } - virtual void unexecute() { - m_layer->removeMask(); - } - }; - - class KisMaskFromSelectionCommand : public KNamedCommand { - typedef KNamedCommand super; - KisPaintLayerSP m_layer; - KisPaintDeviceSP m_maskBefore; - KisPaintDeviceSP m_maskAfter; - KisSelectionSP m_selection; - public: - KisMaskFromSelectionCommand(const TQString& name, KisPaintLayer* layer) - : super(name), m_layer(layer) { - if (m_layer->hasMask()) - m_maskBefore = m_layer->getMask(); - else - m_maskBefore = 0; - m_maskAfter = 0; - if (m_layer->paintDevice()->hasSelection()) - m_selection = m_layer->paintDevice()->selection(); - else - m_selection = 0; - } - virtual void execute() { - if (!m_maskAfter) { - m_layer->createMaskFromSelection(m_selection); - m_maskAfter = m_layer->getMask(); - } else { - m_layer->paintDevice()->deselect(); - m_layer->createMaskFromPaintDevice(m_maskAfter); - } - } - virtual void unexecute() { - m_layer->paintDevice()->setSelection(m_selection); - if (m_maskBefore) - m_layer->createMaskFromPaintDevice(m_maskBefore); - else - m_layer->removeMask(); - } - }; - - class KisMaskToSelectionCommand : public KNamedCommand { - typedef KNamedCommand super; - KisPaintLayerSP m_layer; - KisPaintDeviceSP m_mask; - KisSelectionSP m_selection; - public: - KisMaskToSelectionCommand(const TQString& name, KisPaintLayer* layer) - : super(name), m_layer(layer) { - m_mask = m_layer->getMask(); - if (m_layer->paintDevice()->hasSelection()) - m_selection = m_layer->paintDevice()->selection(); - else - m_selection = 0; - } - virtual void execute() { - m_layer->paintDevice()->setSelection(m_layer->getMaskAsSelection()); - m_layer->removeMask(); - } - virtual void unexecute() { - if (m_selection) - m_layer->paintDevice()->setSelection(m_selection); - else - m_layer->paintDevice()->deselect(); - m_layer->createMaskFromPaintDevice(m_mask); - } - }; - - class KisRemoveMaskCommand : public KNamedCommand { - typedef KNamedCommand super; - KisPaintLayerSP m_layer; - KisPaintDeviceSP m_mask; - public: - KisRemoveMaskCommand(const TQString& name, KisPaintLayer* layer) - : super(name), m_layer(layer) { - m_mask = m_layer->getMask(); - } - virtual void execute() { - kdDebug() << k_funcinfo << endl; - m_layer->removeMask(); - } - virtual void unexecute() { - // I hope that if the undo stack unwinds, it will end up here in the right - // state again; taking a deep-copy sounds like wasteful to me - m_layer->createMaskFromPaintDevice(m_mask); - } - }; - - class KisApplyMaskCommand : public KNamedCommand { - typedef KNamedCommand super; - KisPaintLayerSP m_layer; - KisPaintDeviceSP m_mask; - KisPaintDeviceSP m_original; - public: - KisApplyMaskCommand(const TQString& name, KisPaintLayer* layer) - : super(name), m_layer(layer) { - m_mask = m_layer->getMask(); - m_original = new KisPaintDevice(*m_layer->paintDevice()); - } - virtual void execute() { - m_layer->applyMask(); - } - virtual void unexecute() { - // I hope that if the undo stack unwinds, it will end up here in the right - // state again; taking a deep-copy sounds like wasteful to me - KisPainter gc(m_layer->paintDevice()); - int x, y, w, h; - m_layer->paintDevice()->extent(x, y, w, h); - - gc.bitBlt(x, y, COMPOSITE_COPY, m_original, OPACITY_OPAQUE, x, y, w, h); - gc.end(); - - m_layer->createMaskFromPaintDevice(m_mask); - } - }; -} - -KNamedCommand* KisPaintLayer::createMaskCommand() { - return new KisCreateMaskCommand(i18n("Create Layer Mask"), this); -} - -KNamedCommand* KisPaintLayer::maskFromSelectionCommand() { - return new KisMaskFromSelectionCommand(i18n("Mask From Selection"), this); -} - -KNamedCommand* KisPaintLayer::maskToSelectionCommand() { - return new KisMaskToSelectionCommand(i18n("Mask to Selection"), this); -} - - -KNamedCommand* KisPaintLayer::removeMaskCommand() { - return new KisRemoveMaskCommand(i18n("Remove Layer Mask"), this); -} - -KNamedCommand* KisPaintLayer::applyMaskCommand() { - return new KisApplyMaskCommand(i18n("Apply Layer Mask"), this); -} - - -#include "kis_paint_layer.moc" diff --git a/chalk/core/kis_paint_layer.cpp b/chalk/core/kis_paint_layer.cpp new file mode 100644 index 00000000..a4c5412d --- /dev/null +++ b/chalk/core/kis_paint_layer.cpp @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2005 Casper Boemann + * Copyright (c) 2006 Bart Coppens + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. + */ + +#include +#include + +#include "kis_debug_areas.h" +#include "kis_image.h" +#include "kis_paint_layer.h" +#include "kis_selection.h" +#include "kis_painter.h" +#include "kis_undo_adapter.h" +#include "kis_iterators_pixel.h" +#include "kis_paint_device.h" +#include "kis_meta_registry.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_datamanager.h" +#include "kis_undo_adapter.h" + +KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity, KisPaintDeviceSP dev) + : super(img, name, opacity) +{ + Q_ASSERT(img); + Q_ASSERT(dev); + m_paintdev = dev; + m_mask = 0; + m_maskAsSelection = 0; + m_paintdev->setParentLayer(this); + m_renderMask = false; + m_editMask = true; +} + + +KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity) + : super(img, name, opacity) +{ + Q_ASSERT(img); + m_paintdev = new KisPaintDevice(this, img->colorSpace(), name.latin1()); + m_mask = 0; + m_maskAsSelection = 0; + m_renderMask = false; + m_editMask = true; +} + +KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity, KisColorSpace * colorSpace) + : super(img, name, opacity) +{ + Q_ASSERT(img); + Q_ASSERT(colorSpace); + m_paintdev = new KisPaintDevice(this, colorSpace, name.latin1()); + m_mask = 0; + m_maskAsSelection = 0; + m_renderMask = false; + m_editMask = true; +} + +KisPaintLayer::KisPaintLayer(const KisPaintLayer& rhs) : + KisLayer(rhs), KisLayerSupportsIndirectPainting(rhs) +{ + m_paintdev = new KisPaintDevice( *rhs.m_paintdev.data() ); + m_paintdev->setParentLayer(this); + if (rhs.hasMask()) { + m_mask = new KisPaintDevice(*rhs.m_mask.data()); + m_mask->setParentLayer(this); + } + m_renderMask = rhs.m_renderMask; + m_editMask = rhs.m_editMask; +} + +KisLayerSP KisPaintLayer::clone() const +{ + return new KisPaintLayer(*this); +} + +KisPaintLayer::~KisPaintLayer() +{ + if (m_paintdev != 0) { + m_paintdev->setParentLayer(0); + } + if (m_mask != 0) { + m_mask->setParentLayer(0); + } +} + +void KisPaintLayer::paintSelection(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + if (m_paintdev && m_paintdev->hasSelection()) { + m_paintdev->selection()->paintSelection(img, x, y, w, h); + } else if (m_mask && m_editMask && m_mask->hasSelection()) { + m_mask->selection()->paintSelection(img, x, y, w, h); + } +} + +void KisPaintLayer::paintSelection(TQImage &img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize) +{ + if (m_paintdev && m_paintdev->hasSelection()) { + m_paintdev->selection()->paintSelection(img, scaledImageRect, scaledImageSize, imageSize); + } else if (m_mask && m_editMask && m_mask->hasSelection()) { + m_mask->selection()->paintSelection(img, scaledImageRect, scaledImageSize, imageSize); + } +} + +void KisPaintLayer::paintMaskInactiveLayers(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + uchar *j = img.bits(); + + KisColorSpace *cs = m_paintdev->colorSpace(); + + for (TQ_INT32 y2 = y; y2 < h + y; ++y2) { + KisHLineIteratorPixel it = m_paintdev->createHLineIterator(x, y2, w, false); + while ( ! it.isDone()) { + TQ_UINT8 s = cs->getAlpha(it.rawData()); + if(s==0) + { + TQ_UINT8 g = (*(j + 0) + *(j + 1 ) + *(j + 2 )) / 9; + + *(j+0) = 128+g ; + *(j+1) = 165+g; + *(j+2) = 128+g; + } + j+=4; + ++it; + } + } +} + +TQImage KisPaintLayer::createThumbnail(TQ_INT32 w, TQ_INT32 h) +{ + if (m_paintdev) + return m_paintdev->createThumbnail(w, h); + else + return TQImage(); +} + + +TQ_INT32 KisPaintLayer::x() const { + if (m_paintdev) + return m_paintdev->getX(); + else return 0; +} + +void KisPaintLayer::setX(TQ_INT32 x) +{ + if (m_paintdev) + m_paintdev->setX(x); +} + +TQ_INT32 KisPaintLayer::y() const { + if (m_paintdev) + return m_paintdev->getY(); + else + return 0; +} + +void KisPaintLayer::setY(TQ_INT32 y) { + if (m_paintdev) + m_paintdev->setY(y); +} + +TQRect KisPaintLayer::extent() const { + if (m_paintdev) + return m_paintdev->extent(); + else + return TQRect(); +} + +TQRect KisPaintLayer::exactBounds() const { + if (m_paintdev) + return m_paintdev->exactBounds(); + else + return TQRect(); +} + +void KisPaintLayer::removeMask() { + if (!hasMask()) + return; + + m_mask->setParentLayer(0); + m_mask = 0; + m_maskAsSelection = 0; + setDirty(); + + emit sigMaskInfoChanged(); +} + +// ### XXX Do we apply the mask outside the image boundaries too? I'd say no, but I'm not sure +void KisPaintLayer::applyMask() { + if (!hasMask()) + return; + + int x, y, w, h; + m_paintdev->extent(x, y, w, h); + + // A bit slow; but it works + KisPaintDeviceSP temp = new KisPaintDevice(m_paintdev->colorSpace()); + KisPainter gc(temp); + gc.bltSelection(x, y, COMPOSITE_OVER, m_paintdev, m_maskAsSelection, OPACITY_OPAQUE, x, y, w, h); + gc.end(); + gc.begin(m_paintdev); + gc.bitBlt(x, y, COMPOSITE_COPY, temp, OPACITY_OPAQUE, x, y, w, h); + gc.end(); + + removeMask(); +} + +KisPaintDeviceSP KisPaintLayer::createMask() { + if (hasMask()) + return m_mask; + + kdDebug() << k_funcinfo << endl; + // Grey8 nicely fits our needs of being intuitively comparable to other apps' + // mask layer interfaces. It does have an alpha component though, which is a bit + // less appropriate in this context. + m_mask = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry() + ->getColorSpace(KisID("GRAYA"), 0)); + + genericMaskCreationHelper(); + + return m_mask; +} + +// FIXME If from is a paint device is not grey8!! +void KisPaintLayer::createMaskFromPaintDevice(KisPaintDeviceSP from) { + if (hasMask()) + return; // Or overwrite? XXX + + kdDebug() << k_funcinfo << endl; + m_mask = from; // KisPaintDevice(*from); XXX + + genericMaskCreationHelper(); +} + +void KisPaintLayer::createMaskFromSelection(KisSelectionSP from) { + kdDebug() << k_funcinfo << endl; + m_mask = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry() + ->getColorSpace(KisID("GRAYA"), 0)); + m_mask->setParentLayer(this); + + m_maskAsSelection = new KisSelection(); // Anonymous selection is good enough + + // Default pixel is opaque white == don't mask? + TQ_UINT8 const defPixel[] = { 255, 255 }; + m_mask->dataManager()->setDefaultPixel(defPixel); + + if (from) { + TQRect r(extent()); + + int w = r.width(); + int h = r.height(); + for (int y = r.y(); y < h; y++) { + KisHLineIteratorPixel srcIt = from->createHLineIterator(r.x(), y, w, false); + KisHLineIteratorPixel dstIt = m_mask->createHLineIterator(r.x(), y, w, true); + + while(!dstIt.isDone()) { + // XXX same remark as in convertMaskToSelection + *dstIt.rawData() = *srcIt.rawData(); + ++srcIt; + ++dstIt; + } + } + } + + convertMaskToSelection(extent()); + m_paintdev->deselect(); + + setDirty(); + emit sigMaskInfoChanged(); +} + +KisPaintDeviceSP KisPaintLayer::getMask() { + createMask(); + kdDebug() << k_funcinfo << endl; + return m_mask; +} + +KisSelectionSP KisPaintLayer::getMaskAsSelection() { + createMask(); + kdDebug() << k_funcinfo << endl; + return m_maskAsSelection; +} + +void KisPaintLayer::setEditMask(bool b) { + m_editMask = b; + emit sigMaskInfoChanged(); +} + +void KisPaintLayer::setRenderMask(bool b) { + m_renderMask = b; + + if (hasMask()) + setDirty(); + + emit sigMaskInfoChanged(); +} + +void KisPaintLayer::convertMaskToSelection(const TQRect& r) { + KisRectIteratorPixel srcIt = m_mask->createRectIterator(r.x(), r.y(), + r.width(), r.height(), false); + KisRectIteratorPixel dstIt = m_maskAsSelection->createRectIterator(r.x(), r.y(), + r.width(), r.height(), true); + + while(!dstIt.isDone()) { + // src is grey8 (grey + alpha), dst is alpha8. We convert the grey value to + // alpha8 manually and ignore the alpha (that's why we don't convert using default + // functions, and interpret the data raw!) [ XXX ] + *dstIt.rawData() = *srcIt.rawData(); + ++srcIt; + ++dstIt; + } +} + +void KisPaintLayer::genericMaskCreationHelper() { + m_mask->setParentLayer(this); + + m_maskAsSelection = new KisSelection(); // Anonymous selection is good enough + + // Default pixel is opaque white == don't mask? + TQ_UINT8 const defPixel[] = { 255, 255 }; + m_mask->dataManager()->setDefaultPixel(defPixel); + + setDirty(); + emit sigMaskInfoChanged(); +} + +void KisPaintLayer::setDirty(bool propagate) { + if (hasMask()) + convertMaskToSelection(extent()); + super::setDirty(propagate); +} + +void KisPaintLayer::setDirty(const TQRect & rect, bool propagate) { + if (hasMask()) + convertMaskToSelection(rect); + super::setDirty(rect, propagate); +} + +// Undoable versions code +namespace { + class KisCreateMaskCommand : public KNamedCommand { + typedef KNamedCommand super; + KisPaintLayerSP m_layer; + KisPaintDeviceSP m_mask; + public: + KisCreateMaskCommand(const TQString& name, KisPaintLayer* layer) + : super(name), m_layer(layer) {} + virtual void execute() { + kdDebug() << k_funcinfo << endl; + if (!m_mask) + m_mask = m_layer->createMask(); + else + m_layer->createMaskFromPaintDevice(m_mask); + } + virtual void unexecute() { + m_layer->removeMask(); + } + }; + + class KisMaskFromSelectionCommand : public KNamedCommand { + typedef KNamedCommand super; + KisPaintLayerSP m_layer; + KisPaintDeviceSP m_maskBefore; + KisPaintDeviceSP m_maskAfter; + KisSelectionSP m_selection; + public: + KisMaskFromSelectionCommand(const TQString& name, KisPaintLayer* layer) + : super(name), m_layer(layer) { + if (m_layer->hasMask()) + m_maskBefore = m_layer->getMask(); + else + m_maskBefore = 0; + m_maskAfter = 0; + if (m_layer->paintDevice()->hasSelection()) + m_selection = m_layer->paintDevice()->selection(); + else + m_selection = 0; + } + virtual void execute() { + if (!m_maskAfter) { + m_layer->createMaskFromSelection(m_selection); + m_maskAfter = m_layer->getMask(); + } else { + m_layer->paintDevice()->deselect(); + m_layer->createMaskFromPaintDevice(m_maskAfter); + } + } + virtual void unexecute() { + m_layer->paintDevice()->setSelection(m_selection); + if (m_maskBefore) + m_layer->createMaskFromPaintDevice(m_maskBefore); + else + m_layer->removeMask(); + } + }; + + class KisMaskToSelectionCommand : public KNamedCommand { + typedef KNamedCommand super; + KisPaintLayerSP m_layer; + KisPaintDeviceSP m_mask; + KisSelectionSP m_selection; + public: + KisMaskToSelectionCommand(const TQString& name, KisPaintLayer* layer) + : super(name), m_layer(layer) { + m_mask = m_layer->getMask(); + if (m_layer->paintDevice()->hasSelection()) + m_selection = m_layer->paintDevice()->selection(); + else + m_selection = 0; + } + virtual void execute() { + m_layer->paintDevice()->setSelection(m_layer->getMaskAsSelection()); + m_layer->removeMask(); + } + virtual void unexecute() { + if (m_selection) + m_layer->paintDevice()->setSelection(m_selection); + else + m_layer->paintDevice()->deselect(); + m_layer->createMaskFromPaintDevice(m_mask); + } + }; + + class KisRemoveMaskCommand : public KNamedCommand { + typedef KNamedCommand super; + KisPaintLayerSP m_layer; + KisPaintDeviceSP m_mask; + public: + KisRemoveMaskCommand(const TQString& name, KisPaintLayer* layer) + : super(name), m_layer(layer) { + m_mask = m_layer->getMask(); + } + virtual void execute() { + kdDebug() << k_funcinfo << endl; + m_layer->removeMask(); + } + virtual void unexecute() { + // I hope that if the undo stack unwinds, it will end up here in the right + // state again; taking a deep-copy sounds like wasteful to me + m_layer->createMaskFromPaintDevice(m_mask); + } + }; + + class KisApplyMaskCommand : public KNamedCommand { + typedef KNamedCommand super; + KisPaintLayerSP m_layer; + KisPaintDeviceSP m_mask; + KisPaintDeviceSP m_original; + public: + KisApplyMaskCommand(const TQString& name, KisPaintLayer* layer) + : super(name), m_layer(layer) { + m_mask = m_layer->getMask(); + m_original = new KisPaintDevice(*m_layer->paintDevice()); + } + virtual void execute() { + m_layer->applyMask(); + } + virtual void unexecute() { + // I hope that if the undo stack unwinds, it will end up here in the right + // state again; taking a deep-copy sounds like wasteful to me + KisPainter gc(m_layer->paintDevice()); + int x, y, w, h; + m_layer->paintDevice()->extent(x, y, w, h); + + gc.bitBlt(x, y, COMPOSITE_COPY, m_original, OPACITY_OPAQUE, x, y, w, h); + gc.end(); + + m_layer->createMaskFromPaintDevice(m_mask); + } + }; +} + +KNamedCommand* KisPaintLayer::createMaskCommand() { + return new KisCreateMaskCommand(i18n("Create Layer Mask"), this); +} + +KNamedCommand* KisPaintLayer::maskFromSelectionCommand() { + return new KisMaskFromSelectionCommand(i18n("Mask From Selection"), this); +} + +KNamedCommand* KisPaintLayer::maskToSelectionCommand() { + return new KisMaskToSelectionCommand(i18n("Mask to Selection"), this); +} + + +KNamedCommand* KisPaintLayer::removeMaskCommand() { + return new KisRemoveMaskCommand(i18n("Remove Layer Mask"), this); +} + +KNamedCommand* KisPaintLayer::applyMaskCommand() { + return new KisApplyMaskCommand(i18n("Apply Layer Mask"), this); +} + + +#include "kis_paint_layer.moc" diff --git a/chalk/core/kis_painter.cc b/chalk/core/kis_painter.cc deleted file mode 100644 index f3f5c928..00000000 --- a/chalk/core/kis_painter.cc +++ /dev/null @@ -1,928 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * Copyright (c) 2004 Boudewijn Rempt - * Copyright (c) 2004 Clarence Dang - * Copyright (c) 2004 Adrian Page - * Copyright (c) 2004 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include -#include -#include -#include -#include - -#include "tqbrush.h" -#include "tqfontinfo.h" -#include "tqfontmetrics.h" -#include "tqpen.h" -#include "tqregion.h" -#include "tqwmatrix.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "kis_brush.h" -#include "kis_debug_areas.h" -#include "kis_image.h" -#include "kis_layer.h" -#include "kis_paint_device.h" -#include "kis_painter.h" -#include "kis_pattern.h" -#include "kis_rect.h" -#include "kis_colorspace.h" -#include "kis_transaction.h" -#include "kis_types.h" -#include "kis_vec.h" -#include "kis_iterators_pixel.h" -#include "kis_paintop.h" -#include "kis_selection.h" -#include "kis_fill_painter.h" -#include "kis_color.h" - -// Maximum distance from a Bezier control point to the line through the start -// and end points for the curve to be considered flat. -#define BEZIER_FLATNESS_THRESHOLD 0.5 - -KisPainter::KisPainter() -{ - init(); -} - -KisPainter::KisPainter(KisPaintDeviceSP device) -{ - init(); - Q_ASSERT(device); - begin(device); -} - -void KisPainter::init() -{ - m_transaction = 0; - m_paintOp = 0; - m_filter = 0; - m_brush = 0; - m_pattern= 0; - m_opacity = OPACITY_OPAQUE; - m_compositeOp = COMPOSITE_OVER; - m_dab = 0; - m_fillStyle = FillStyleNone; - m_strokeStyle = StrokeStyleBrush; - m_pressure = PRESSURE_MIN; - m_duplicateHealing = false; - m_duplicateHealingRadius = 10; - m_duplicatePerspectiveCorrection = false; - m_varyBrushSpacingWithPressureWhenDrawingALine = true; -} - -KisPainter::~KisPainter() -{ - m_brush = 0; - delete m_paintOp; - end(); -} - -void KisPainter::begin(KisPaintDeviceSP device) -{ - if (!device) return; - - if (m_transaction) - delete m_transaction; - - m_device = device; - m_colorSpace = device->colorSpace(); - m_pixelSize = device->pixelSize(); -} - -KCommand *KisPainter::end() -{ - return endTransaction(); -} - -void KisPainter::beginTransaction(const TQString& customName) -{ - if (m_transaction) - delete m_transaction; - m_transaction = new KisTransaction(customName, m_device); - TQ_CHECK_PTR(m_transaction); -} - -void KisPainter::beginTransaction( KisTransaction* command) -{ - if (m_transaction) - delete m_transaction; - m_transaction = command; -} - - -KCommand *KisPainter::endTransaction() -{ - KCommand *command = m_transaction; - m_transaction = 0; - return command; -} - - -TQRect KisPainter::dirtyRect() { - TQRect r = m_dirtyRect; - m_dirtyRect = TQRect(); - return r; -} - -void KisPainter::bitBlt(TQ_INT32 dx, TQ_INT32 dy, - const KisCompositeOp& op, - KisPaintDeviceSP srcdev, - TQ_UINT8 opacity, - TQ_INT32 sx, TQ_INT32 sy, - TQ_INT32 sw, TQ_INT32 sh) -{ - if (srcdev == 0) { - return; - } - - TQRect srcRect = TQRect(sx, sy, sw, sh); - - if (srcdev->extentIsValid() && op != COMPOSITE_COPY) { - srcRect &= srcdev->extent(); - } - - if (srcRect.isEmpty()) { - return; - } - - dx += srcRect.x() - sx; - dy += srcRect.y() - sy; - - sx = srcRect.x(); - sy = srcRect.y(); - sw = srcRect.width(); - sh = srcRect.height(); - - addDirtyRect(TQRect(dx, dy, sw, sh)); - - KisColorSpace * srcCs = srcdev->colorSpace(); - - TQ_INT32 dstY = dy; - TQ_INT32 srcY = sy; - TQ_INT32 rowsRemaining = sh; - - while (rowsRemaining > 0) { - - TQ_INT32 dstX = dx; - TQ_INT32 srcX = sx; - TQ_INT32 columnsRemaining = sw; - TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1); - TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1); - - TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows); - rows = TQMIN(rows, rowsRemaining); - - while (columnsRemaining > 0) { - - TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1); - TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1); - - TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns); - columns = TQMIN(columns, columnsRemaining); - - TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY); - //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY); - KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false); - const TQ_UINT8 *srcData = srcIt.rawData(); - - //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY); - TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY); - KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true); - TQ_UINT8 *dstData = dstIt.rawData(); - - - m_colorSpace->bitBlt(dstData, - dstRowStride, - srcCs, - srcData, - srcRowStride, - 0, - 0, - opacity, - rows, - columns, - op); - - srcX += columns; - dstX += columns; - columnsRemaining -= columns; - } - - srcY += rows; - dstY += rows; - rowsRemaining -= rows; - } -} - -void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy, - const KisCompositeOp &op, - KisPaintDeviceSP srcdev, - KisSelectionSP seldev, - TQ_UINT8 opacity, - TQ_INT32 sx, TQ_INT32 sy, - TQ_INT32 sw, TQ_INT32 sh) -{ - // Better use a probablistic method than a too slow one - if (seldev->isProbablyTotallyUnselected(TQRect(dx, dy, sw, sh))) { -/* - kdDebug() << "Blitting outside selection rect\n"; - - kdDebug() << "srcdev: " << srcdev << " (" << srcdev->name() << ")" - << ", seldev: " << seldev << " (" << seldev->name() << ")" - << ". dx, dy " << dx << "," << dy - << ". sx, sy : sw, sy " << sx << "," << sy << " : " << sw << "," << sh << endl; -*/ - return; - } - bltMask(dx,dy,op,srcdev,seldev.data(),opacity,sx,sy,sw,sh); -} - -void KisPainter::bltMask(TQ_INT32 dx, TQ_INT32 dy, - const KisCompositeOp &op, - KisPaintDeviceSP srcdev, - KisPaintDeviceSP seldev, - TQ_UINT8 opacity, - TQ_INT32 sx, TQ_INT32 sy, - TQ_INT32 sw, TQ_INT32 sh) - -{ - if (srcdev == 0) return; - - if (seldev == 0) return; - - if (m_device == 0) return; - - - TQRect srcRect = TQRect(sx, sy, sw, sh); - - if (srcdev->extentIsValid() && op != COMPOSITE_COPY) { - srcRect &= srcdev->extent(); - } - - if (srcRect.isEmpty()) { - return; - } - - dx += srcRect.x() - sx; - dy += srcRect.y() - sy; - - sx = srcRect.x(); - sy = srcRect.y(); - sw = srcRect.width(); - sh = srcRect.height(); - - addDirtyRect(TQRect(dx, dy, sw, sh)); - - KisColorSpace * srcCs = srcdev->colorSpace(); - - TQ_INT32 dstY = dy; - TQ_INT32 srcY = sy; - TQ_INT32 rowsRemaining = sh; - - while (rowsRemaining > 0) { - - TQ_INT32 dstX = dx; - TQ_INT32 srcX = sx; - TQ_INT32 columnsRemaining = sw; - TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1); - TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1); - TQ_INT32 numContiguousSelRows = seldev->numContiguousRows(dstY, dstX, dstX + sw - 1); - - TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows); - rows = TQMIN(numContiguousSelRows, rows); - rows = TQMIN(rows, rowsRemaining); - - while (columnsRemaining > 0) { - - TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1); - TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1); - TQ_INT32 numContiguousSelColumns = seldev->numContiguousColumns(dstX, dstY, dstY + rows - 1); - - TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns); - columns = TQMIN(numContiguousSelColumns, columns); - columns = TQMIN(columns, columnsRemaining); - - //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY); - TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY); - KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true); - TQ_UINT8 *dstData = dstIt.rawData(); - - //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY); - TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY); - KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false); - const TQ_UINT8 *srcData = srcIt.rawData(); - - //const TQ_UINT8 *selData = seldev->pixel(dstX, dstY); - TQ_INT32 selRowStride = seldev->rowStride(dstX, dstY); - KisHLineIteratorPixel selIt = seldev->createHLineIterator(dstX, dstY, columns, false); - const TQ_UINT8 *selData = selIt.rawData(); - - m_colorSpace->bitBlt(dstData, - dstRowStride, - srcCs, - srcData, - srcRowStride, - selData, - selRowStride, - opacity, - rows, - columns, - op); - - srcX += columns; - dstX += columns; - columnsRemaining -= columns; - } - - srcY += rows; - dstY += rows; - rowsRemaining -= rows; - } -} - - -void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy, - const KisCompositeOp& op, - KisPaintDeviceSP srcdev, - TQ_UINT8 opacity, - TQ_INT32 sx, TQ_INT32 sy, - TQ_INT32 sw, TQ_INT32 sh) -{ - if (m_device == 0) return; - if (!m_device->hasSelection()) { - bitBlt(dx, dy, op, srcdev, opacity, sx, sy, sw, sh); - } - else - bltSelection(dx,dy,op,srcdev, m_device->selection(),opacity,sx,sy,sw,sh); -} - -double KisPainter::paintLine(const KisPoint & pos1, - const double pressure1, - const double xTilt1, - const double yTilt1, - const KisPoint & pos2, - const double pressure2, - const double xTilt2, - const double yTilt2, - const double inSavedDist) -{ - if (!m_device) return 0; - if (!m_paintOp) return 0; - if (!m_brush) return 0; - - double savedDist = inSavedDist; - KisVector2D end(pos2); - KisVector2D start(pos1); - - KisVector2D dragVec = end - start; - KisVector2D movement = dragVec; - - if (savedDist < 0) { - m_paintOp->paintAt(pos1, KisPaintInformation(pressure1, xTilt1, yTilt1, movement)); - savedDist = 0; - } - - double xSpacing = 0; - double ySpacing = 0; - - if ( m_varyBrushSpacingWithPressureWhenDrawingALine ) { - // XXX: The spacing should vary as the pressure changes along the - // line. - // This is a quick simplification. - xSpacing = m_brush->xSpacing((pressure1 + pressure2) / 2); - ySpacing = m_brush->ySpacing((pressure1 + pressure2) / 2); - } - else { - xSpacing = m_brush->xSpacing( PRESSURE_DEFAULT ); - ySpacing = m_brush->ySpacing( PRESSURE_DEFAULT ); - } - - if (xSpacing < 0.5) { - xSpacing = 0.5; - } - if (ySpacing < 0.5) { - ySpacing = 0.5; - } - - double xScale = 1; - double yScale = 1; - double spacing; - // Scale x or y so that we effectively have a square brush - // and calculate distance in that coordinate space. We reverse this scaling - // before drawing the brush. This produces the correct spacing in both - // x and y directions, even if the brush's aspect ratio is not 1:1. - if (xSpacing > ySpacing) { - yScale = xSpacing / ySpacing; - spacing = xSpacing; - } - else { - xScale = ySpacing / xSpacing; - spacing = ySpacing; - } - - dragVec.setX(dragVec.x() * xScale); - dragVec.setY(dragVec.y() * yScale); - - double newDist = dragVec.length(); - double dist = savedDist + newDist; - double l_savedDist = savedDist; - - if (dist < spacing) { - return dist; - } - - dragVec.normalize(); - KisVector2D step(0, 0); - - while (dist >= spacing) { - if (l_savedDist > 0) { - step += dragVec * (spacing - l_savedDist); - l_savedDist -= spacing; - } - else { - step += dragVec * spacing; - } - - KisPoint p(start.x() + (step.x() / xScale), start.y() + (step.y() / yScale)); - - double distanceMoved = step.length(); - double t = 0; - - if (newDist > DBL_EPSILON) { - t = distanceMoved / newDist; - } - - double pressure = (1 - t) * pressure1 + t * pressure2; - double xTilt = (1 - t) * xTilt1 + t * xTilt2; - double yTilt = (1 - t) * yTilt1 + t * yTilt2; - - m_paintOp->paintAt(p, KisPaintInformation(pressure, xTilt, yTilt, movement)); - dist -= spacing; - } - - if (dist > 0) - return dist; - else - return 0; -} - -void KisPainter::paintPolyline (const vKisPoint &points, - int index, int numPoints) -{ - if (index >= (int) points.count ()) - return; - - if (numPoints < 0) - numPoints = points.count (); - - if (index + numPoints > (int) points.count ()) - numPoints = points.count () - index; - - - for (int i = index; i < index + numPoints - 1; i++) - { - paintLine (points [index], 0/*pressure*/, 0, 0, points [index + 1], - 0/*pressure*/, 0, 0); - } -} - -void KisPainter::getBezierCurvePoints(const KisPoint &pos1, - const KisPoint &control1, - const KisPoint &control2, - const KisPoint &pos2, - vKisPoint& points) -{ - double d1 = pointToLineDistance(control1, pos1, pos2); - double d2 = pointToLineDistance(control2, pos1, pos2); - - if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) { - points.push_back(pos1); - } else { - // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 - KisVector2D p1 = pos1; - KisVector2D p2 = control1; - KisVector2D p3 = control2; - KisVector2D p4 = pos2; - - KisVector2D l2 = (p1 + p2) / 2; - KisVector2D h = (p2 + p3) / 2; - KisVector2D l3 = (l2 + h) / 2; - KisVector2D r3 = (p3 + p4) / 2; - KisVector2D r2 = (h + r3) / 2; - KisVector2D l4 = (l3 + r2) / 2; - KisVector2D r1 = l4; - KisVector2D l1 = p1; - KisVector2D r4 = p4; - - getBezierCurvePoints(l1.toKisPoint(), l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), points); - getBezierCurvePoints(r1.toKisPoint(), r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), points); - } -} - -double KisPainter::paintBezierCurve(const KisPoint &pos1, - const double pressure1, - const double xTilt1, - const double yTilt1, - const KisPoint &control1, - const KisPoint &control2, - const KisPoint &pos2, - const double pressure2, - const double xTilt2, - const double yTilt2, - const double savedDist) -{ - double newDistance; - double d1 = pointToLineDistance(control1, pos1, pos2); - double d2 = pointToLineDistance(control2, pos1, pos2); - - if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) { - newDistance = paintLine(pos1, pressure1, xTilt1, yTilt1, pos2, pressure2, xTilt2, yTilt2, savedDist); - } else { - // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 - KisVector2D p1 = pos1; - KisVector2D p2 = control1; - KisVector2D p3 = control2; - KisVector2D p4 = pos2; - - KisVector2D l2 = (p1 + p2) / 2; - KisVector2D h = (p2 + p3) / 2; - KisVector2D l3 = (l2 + h) / 2; - KisVector2D r3 = (p3 + p4) / 2; - KisVector2D r2 = (h + r3) / 2; - KisVector2D l4 = (l3 + r2) / 2; - KisVector2D r1 = l4; - KisVector2D l1 = p1; - KisVector2D r4 = p4; - - double midPressure = (pressure1 + pressure2) / 2; - double midXTilt = (xTilt1 + xTilt2) / 2; - double midYTilt = (yTilt1 + yTilt2) / 2; - - newDistance = paintBezierCurve(l1.toKisPoint(), pressure1, xTilt1, yTilt1, - l2.toKisPoint(), l3.toKisPoint(), - l4.toKisPoint(), midPressure, midXTilt, midYTilt, - savedDist); - newDistance = paintBezierCurve(r1.toKisPoint(), midPressure, midXTilt, midYTilt, - r2.toKisPoint(), - r3.toKisPoint(), - r4.toKisPoint(), pressure2, xTilt2, yTilt2, newDistance); - } - - return newDistance; -} - -void KisPainter::paintRect (const KisPoint &startPoint, - const KisPoint &endPoint, - const double /*pressure*/, - const double /*xTilt*/, - const double /*yTilt*/) -{ - KoRect normalizedRect = KisRect (startPoint, endPoint).normalize (); - - vKisPoint points; - - points.push_back(normalizedRect.topLeft()); - points.push_back(normalizedRect.bottomLeft()); - points.push_back(normalizedRect.bottomRight()); - points.push_back(normalizedRect.topRight()); - - paintPolygon(points); -} - -void KisPainter::paintEllipse (const KisPoint &startPoint, - const KisPoint &endPoint, - const double /*pressure*/, - const double /*xTilt*/, - const double /*yTilt*/) -{ - KisRect r = KisRect(startPoint, endPoint).normalize(); - - // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation. - // kappa = (4/3*(sqrt(2)-1)) - const double kappa = 0.5522847498; - const double lx = (r.width() / 2) * kappa; - const double ly = (r.height() / 2) * kappa; - - KisPoint center = r.center(); - - KisPoint p0(r.left(), center.y()); - KisPoint p1(r.left(), center.y() - ly); - KisPoint p2(center.x() - lx, r.top()); - KisPoint p3(center.x(), r.top()); - - vKisPoint points; - - getBezierCurvePoints(p0, p1, p2, p3, points); - - KisPoint p4(center.x() + lx, r.top()); - KisPoint p5(r.right(), center.y() - ly); - KisPoint p6(r.right(), center.y()); - - getBezierCurvePoints(p3, p4, p5, p6, points); - - KisPoint p7(r.right(), center.y() + ly); - KisPoint p8(center.x() + lx, r.bottom()); - KisPoint p9(center.x(), r.bottom()); - - getBezierCurvePoints(p6, p7, p8, p9, points); - - KisPoint p10(center.x() - lx, r.bottom()); - KisPoint p11(r.left(), center.y() + ly); - - getBezierCurvePoints(p9, p10, p11, p0, points); - - paintPolygon(points); -} - -void KisPainter::paintAt(const KisPoint & pos, - const double pressure, - const double xTilt, - const double yTilt) -{ - if (!m_paintOp) return; - m_paintOp->paintAt(pos, KisPaintInformation(pressure, xTilt, yTilt, KisVector2D())); -} - -double KisPainter::pointToLineDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1) -{ - double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y())); - double distance = 0; - - if (lineLength > DBL_EPSILON) { - distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength; - distance = fabs(distance); - } - - return distance; -} - -/* - * Concave Polygon Scan Conversion - * by Paul Heckbert - * from "Graphics Gems", Academic Press, 1990 - */ - -/* - * concave: scan convert nvert-sided concave non-simple polygon with vertices at - * (point[i].x, point[i].y) for i in [0..nvert-1] within the window win by - * calling spanproc for each visible span of pixels. - * Polygon can be clockwise or counterclockwise. - * Algorithm does uniform point sampling at pixel centers. - * Inside-outside test done by Jordan's rule: a point is considered inside if - * an emanating ray intersects the polygon an odd number of times. - * drawproc should fill in pixels from xl to xr inclusive on scanline y, - * e.g: - * drawproc(y, xl, xr) - * int y, xl, xr; - * { - * int x; - * for (x=xl; x<=xr; x++) - * pixel_write(x, y, pixelvalue); - * } - * - * Paul Heckbert 30 June 81, 18 Dec 89 - */ - -typedef struct { /* a polygon edge */ - double x; /* x coordinate of edge's intersection with current scanline */ - double dx; /* change in x with respect to y */ - int i; /* edge number: edge i goes from pt[i] to pt[i+1] */ -} Edge; - -static int n; /* number of vertices */ -static const KisPoint *pt; /* vertices */ - -static int nact; /* number of active edges */ -static Edge *active; /* active edge list:edges crossing scanline y */ - -/* comparison routines for qsort */ -static int compare_ind(const void *pu, const void *pv) -{ - const int *u = static_cast(pu); - const int *v = static_cast(pv); - - return pt[*u].y() <= pt[*v].y() ? -1 : 1; -} - -static int compare_active(const void *pu, const void *pv) -{ - const Edge *u = static_cast(pu); - const Edge *v = static_cast(pv); - - return u->x <= v->x ? -1 : 1; -} - -static void cdelete(int i) /* remove edge i from active list */ -{ - int j; - - for (j=0; j=nact) return; /* edge not in active list; happens at win->y0*/ - nact--; - bcopy(&active[j+1], &active[j], (nact-j)*sizeof active[0]); -} - -static void cinsert(int i, int y) /* append edge i to end of active list */ -{ - int j; - double dx; - const KisPoint *p, *q; - - j = ix()-p->x())/(q->y()-p->y()); - active[nact].x = dx*(y+.5-p->y())+p->x(); - active[nact].i = i; - nact++; -} - -void KisPainter::fillPolygon(const vKisPoint& points, FillStyle fillStyle) -{ - int nvert = points.count(); - int k, y0, y1, y, i, j, xl, xr; - int *ind; /* list of vertex indices, sorted by pt[ind[j]].y */ - - n = nvert; - pt = &(points[0]); - if (n<3) return; - if (fillStyle == FillStyleNone) { - return; - } - - ind = new int[n]; - TQ_CHECK_PTR(ind); - active = new Edge[n]; - TQ_CHECK_PTR(active); - - /* create y-sorted array of indices ind[k] into vertex list */ - for (k=0; k(ceil(pt[ind[0]].y()-.5)); /* ymin of polygon */ - y1 = static_cast(floor(pt[ind[n-1]].y()-.5)); /* ymax of polygon */ - - int x0 = INT_MAX; - int x1 = INT_MIN; - - for (int i = 0; i < nvert; i++) { - int pointHighX = static_cast(ceil(points[i].x() - 0.5)); - int pointLowX = static_cast(floor(points[i].x() - 0.5)); - - if (pointLowX < x0) { - x0 = pointLowX; - } - if (pointHighX > x1) { - x1 = pointHighX; - } - } - - // Fill the polygon bounding rectangle with the required contents then we'll - // create a mask for the actual polygon coverage. - - KisPaintDeviceSP polygon = new KisPaintDevice(m_device->colorSpace(), "polygon"); - TQ_CHECK_PTR(polygon); - - KisFillPainter fillPainter(polygon); - TQRect boundingRectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1); - - // Clip to the image bounds. - if (m_device->image()) { - boundingRectangle &= m_device->image()->bounds(); - } - - switch (fillStyle) { - default: - // Fall through - case FillStyleGradient: - // Currently unsupported, fall through - case FillStyleStrokes: - // Currently unsupported, fall through - kdWarning(DBG_AREA_CORE) << "Unknown or unsupported fill style in fillPolygon\n"; - case FillStyleForegroundColor: - fillPainter.fillRect(boundingRectangle, paintColor(), OPACITY_OPAQUE); - break; - case FillStyleBackgroundColor: - fillPainter.fillRect(boundingRectangle, backgroundColor(), OPACITY_OPAQUE); - break; - case FillStylePattern: - Q_ASSERT(m_pattern != 0); - fillPainter.fillRect(boundingRectangle, m_pattern); - break; - } - - KisSelectionSP polygonMask = new KisSelection(polygon); - - for (y=y0; y<=y1; y++) { /* step through scanlines */ - /* scanline y is at y+.5 in continuous coordinates */ - - /* check vertices between previous scanline and current one, if any */ - for (; k0 ? i-1 : n-1; /* vertex previous to i */ - if (pt[j].y() <= y-.5) /* old edge, remove from active list */ - cdelete(j); - else if (pt[j].y() > y+.5) /* new edge, add to active list */ - cinsert(j, y); - j = i y+.5) /* new edge, add to active list */ - cinsert(i, y); - } - - /* sort active edge list by active[j].x */ - qsort(active, nact, sizeof active[0], compare_active); - - /* draw horizontal segments for scanline y */ - for (j=0; j(ceil(active[j].x-.5)); /* left end of span */ - xr = static_cast(floor(active[j+1].x-.5)); /* right end of span */ - - if (xl<=xr) { - KisHLineIterator it = polygonMask->createHLineIterator(xl, y, xr - xl + 1, true); - - while (!it.isDone()) { - // We're using a selection here, that means alpha colorspace, that means one byte. - it.rawData()[0] = MAX_SELECTED; - ++it; - } - } - - active[j].x += active[j].dx; /* increment edge coords */ - active[j+1].x += active[j+1].dx; - } - } - delete [] ind; - delete [] active; - - polygon->applySelectionMask(polygonMask); - - TQRect r = polygon->extent(); - - // The strokes for the outline may have already added updated the dirtyrect, but it can't hurt, - // and if we're painting without outlines, then there will be no dirty rect. Let's do it ourselves... - // addDirtyRect( r ); // XXX the bltSelection will add to the dirtyrect - - bltSelection(r.x(), r.y(), compositeOp(), polygon, opacity(), r.x(), r.y(), r.width(), r.height()); -} - -void KisPainter::paintPolygon(const vKisPoint& points) -{ - if (m_fillStyle != FillStyleNone) { - fillPolygon(points, m_fillStyle); - } - - if (m_strokeStyle != StrokeStyleNone) { - if (points.count() > 1) { - double distance = -1; - - for (uint i = 0; i < points.count() - 1; i++) { - distance = paintLine(points[i], PRESSURE_DEFAULT, 0, 0, points[i + 1], PRESSURE_DEFAULT, 0, 0, distance); - } - paintLine(points[points.count() - 1], PRESSURE_DEFAULT, 0, 0, points[0], PRESSURE_DEFAULT, 0, 0, distance); - } - } -} - diff --git a/chalk/core/kis_painter.cpp b/chalk/core/kis_painter.cpp new file mode 100644 index 00000000..f3f5c928 --- /dev/null +++ b/chalk/core/kis_painter.cpp @@ -0,0 +1,928 @@ +/* + * Copyright (c) 2002 Patrick Julien + * Copyright (c) 2004 Boudewijn Rempt + * Copyright (c) 2004 Clarence Dang + * Copyright (c) 2004 Adrian Page + * Copyright (c) 2004 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include +#include +#include + +#include "tqbrush.h" +#include "tqfontinfo.h" +#include "tqfontmetrics.h" +#include "tqpen.h" +#include "tqregion.h" +#include "tqwmatrix.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "kis_brush.h" +#include "kis_debug_areas.h" +#include "kis_image.h" +#include "kis_layer.h" +#include "kis_paint_device.h" +#include "kis_painter.h" +#include "kis_pattern.h" +#include "kis_rect.h" +#include "kis_colorspace.h" +#include "kis_transaction.h" +#include "kis_types.h" +#include "kis_vec.h" +#include "kis_iterators_pixel.h" +#include "kis_paintop.h" +#include "kis_selection.h" +#include "kis_fill_painter.h" +#include "kis_color.h" + +// Maximum distance from a Bezier control point to the line through the start +// and end points for the curve to be considered flat. +#define BEZIER_FLATNESS_THRESHOLD 0.5 + +KisPainter::KisPainter() +{ + init(); +} + +KisPainter::KisPainter(KisPaintDeviceSP device) +{ + init(); + Q_ASSERT(device); + begin(device); +} + +void KisPainter::init() +{ + m_transaction = 0; + m_paintOp = 0; + m_filter = 0; + m_brush = 0; + m_pattern= 0; + m_opacity = OPACITY_OPAQUE; + m_compositeOp = COMPOSITE_OVER; + m_dab = 0; + m_fillStyle = FillStyleNone; + m_strokeStyle = StrokeStyleBrush; + m_pressure = PRESSURE_MIN; + m_duplicateHealing = false; + m_duplicateHealingRadius = 10; + m_duplicatePerspectiveCorrection = false; + m_varyBrushSpacingWithPressureWhenDrawingALine = true; +} + +KisPainter::~KisPainter() +{ + m_brush = 0; + delete m_paintOp; + end(); +} + +void KisPainter::begin(KisPaintDeviceSP device) +{ + if (!device) return; + + if (m_transaction) + delete m_transaction; + + m_device = device; + m_colorSpace = device->colorSpace(); + m_pixelSize = device->pixelSize(); +} + +KCommand *KisPainter::end() +{ + return endTransaction(); +} + +void KisPainter::beginTransaction(const TQString& customName) +{ + if (m_transaction) + delete m_transaction; + m_transaction = new KisTransaction(customName, m_device); + TQ_CHECK_PTR(m_transaction); +} + +void KisPainter::beginTransaction( KisTransaction* command) +{ + if (m_transaction) + delete m_transaction; + m_transaction = command; +} + + +KCommand *KisPainter::endTransaction() +{ + KCommand *command = m_transaction; + m_transaction = 0; + return command; +} + + +TQRect KisPainter::dirtyRect() { + TQRect r = m_dirtyRect; + m_dirtyRect = TQRect(); + return r; +} + +void KisPainter::bitBlt(TQ_INT32 dx, TQ_INT32 dy, + const KisCompositeOp& op, + KisPaintDeviceSP srcdev, + TQ_UINT8 opacity, + TQ_INT32 sx, TQ_INT32 sy, + TQ_INT32 sw, TQ_INT32 sh) +{ + if (srcdev == 0) { + return; + } + + TQRect srcRect = TQRect(sx, sy, sw, sh); + + if (srcdev->extentIsValid() && op != COMPOSITE_COPY) { + srcRect &= srcdev->extent(); + } + + if (srcRect.isEmpty()) { + return; + } + + dx += srcRect.x() - sx; + dy += srcRect.y() - sy; + + sx = srcRect.x(); + sy = srcRect.y(); + sw = srcRect.width(); + sh = srcRect.height(); + + addDirtyRect(TQRect(dx, dy, sw, sh)); + + KisColorSpace * srcCs = srcdev->colorSpace(); + + TQ_INT32 dstY = dy; + TQ_INT32 srcY = sy; + TQ_INT32 rowsRemaining = sh; + + while (rowsRemaining > 0) { + + TQ_INT32 dstX = dx; + TQ_INT32 srcX = sx; + TQ_INT32 columnsRemaining = sw; + TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1); + TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1); + + TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows); + rows = TQMIN(rows, rowsRemaining); + + while (columnsRemaining > 0) { + + TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1); + TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1); + + TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns); + columns = TQMIN(columns, columnsRemaining); + + TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY); + //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY); + KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false); + const TQ_UINT8 *srcData = srcIt.rawData(); + + //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY); + TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY); + KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true); + TQ_UINT8 *dstData = dstIt.rawData(); + + + m_colorSpace->bitBlt(dstData, + dstRowStride, + srcCs, + srcData, + srcRowStride, + 0, + 0, + opacity, + rows, + columns, + op); + + srcX += columns; + dstX += columns; + columnsRemaining -= columns; + } + + srcY += rows; + dstY += rows; + rowsRemaining -= rows; + } +} + +void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy, + const KisCompositeOp &op, + KisPaintDeviceSP srcdev, + KisSelectionSP seldev, + TQ_UINT8 opacity, + TQ_INT32 sx, TQ_INT32 sy, + TQ_INT32 sw, TQ_INT32 sh) +{ + // Better use a probablistic method than a too slow one + if (seldev->isProbablyTotallyUnselected(TQRect(dx, dy, sw, sh))) { +/* + kdDebug() << "Blitting outside selection rect\n"; + + kdDebug() << "srcdev: " << srcdev << " (" << srcdev->name() << ")" + << ", seldev: " << seldev << " (" << seldev->name() << ")" + << ". dx, dy " << dx << "," << dy + << ". sx, sy : sw, sy " << sx << "," << sy << " : " << sw << "," << sh << endl; +*/ + return; + } + bltMask(dx,dy,op,srcdev,seldev.data(),opacity,sx,sy,sw,sh); +} + +void KisPainter::bltMask(TQ_INT32 dx, TQ_INT32 dy, + const KisCompositeOp &op, + KisPaintDeviceSP srcdev, + KisPaintDeviceSP seldev, + TQ_UINT8 opacity, + TQ_INT32 sx, TQ_INT32 sy, + TQ_INT32 sw, TQ_INT32 sh) + +{ + if (srcdev == 0) return; + + if (seldev == 0) return; + + if (m_device == 0) return; + + + TQRect srcRect = TQRect(sx, sy, sw, sh); + + if (srcdev->extentIsValid() && op != COMPOSITE_COPY) { + srcRect &= srcdev->extent(); + } + + if (srcRect.isEmpty()) { + return; + } + + dx += srcRect.x() - sx; + dy += srcRect.y() - sy; + + sx = srcRect.x(); + sy = srcRect.y(); + sw = srcRect.width(); + sh = srcRect.height(); + + addDirtyRect(TQRect(dx, dy, sw, sh)); + + KisColorSpace * srcCs = srcdev->colorSpace(); + + TQ_INT32 dstY = dy; + TQ_INT32 srcY = sy; + TQ_INT32 rowsRemaining = sh; + + while (rowsRemaining > 0) { + + TQ_INT32 dstX = dx; + TQ_INT32 srcX = sx; + TQ_INT32 columnsRemaining = sw; + TQ_INT32 numContiguousDstRows = m_device->numContiguousRows(dstY, dstX, dstX + sw - 1); + TQ_INT32 numContiguousSrcRows = srcdev->numContiguousRows(srcY, srcX, srcX + sw - 1); + TQ_INT32 numContiguousSelRows = seldev->numContiguousRows(dstY, dstX, dstX + sw - 1); + + TQ_INT32 rows = TQMIN(numContiguousDstRows, numContiguousSrcRows); + rows = TQMIN(numContiguousSelRows, rows); + rows = TQMIN(rows, rowsRemaining); + + while (columnsRemaining > 0) { + + TQ_INT32 numContiguousDstColumns = m_device->numContiguousColumns(dstX, dstY, dstY + rows - 1); + TQ_INT32 numContiguousSrcColumns = srcdev->numContiguousColumns(srcX, srcY, srcY + rows - 1); + TQ_INT32 numContiguousSelColumns = seldev->numContiguousColumns(dstX, dstY, dstY + rows - 1); + + TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns); + columns = TQMIN(numContiguousSelColumns, columns); + columns = TQMIN(columns, columnsRemaining); + + //TQ_UINT8 *dstData = m_device->writablePixel(dstX, dstY); + TQ_INT32 dstRowStride = m_device->rowStride(dstX, dstY); + KisHLineIteratorPixel dstIt = m_device->createHLineIterator(dstX, dstY, columns, true); + TQ_UINT8 *dstData = dstIt.rawData(); + + //const TQ_UINT8 *srcData = srcdev->pixel(srcX, srcY); + TQ_INT32 srcRowStride = srcdev->rowStride(srcX, srcY); + KisHLineIteratorPixel srcIt = srcdev->createHLineIterator(srcX, srcY, columns, false); + const TQ_UINT8 *srcData = srcIt.rawData(); + + //const TQ_UINT8 *selData = seldev->pixel(dstX, dstY); + TQ_INT32 selRowStride = seldev->rowStride(dstX, dstY); + KisHLineIteratorPixel selIt = seldev->createHLineIterator(dstX, dstY, columns, false); + const TQ_UINT8 *selData = selIt.rawData(); + + m_colorSpace->bitBlt(dstData, + dstRowStride, + srcCs, + srcData, + srcRowStride, + selData, + selRowStride, + opacity, + rows, + columns, + op); + + srcX += columns; + dstX += columns; + columnsRemaining -= columns; + } + + srcY += rows; + dstY += rows; + rowsRemaining -= rows; + } +} + + +void KisPainter::bltSelection(TQ_INT32 dx, TQ_INT32 dy, + const KisCompositeOp& op, + KisPaintDeviceSP srcdev, + TQ_UINT8 opacity, + TQ_INT32 sx, TQ_INT32 sy, + TQ_INT32 sw, TQ_INT32 sh) +{ + if (m_device == 0) return; + if (!m_device->hasSelection()) { + bitBlt(dx, dy, op, srcdev, opacity, sx, sy, sw, sh); + } + else + bltSelection(dx,dy,op,srcdev, m_device->selection(),opacity,sx,sy,sw,sh); +} + +double KisPainter::paintLine(const KisPoint & pos1, + const double pressure1, + const double xTilt1, + const double yTilt1, + const KisPoint & pos2, + const double pressure2, + const double xTilt2, + const double yTilt2, + const double inSavedDist) +{ + if (!m_device) return 0; + if (!m_paintOp) return 0; + if (!m_brush) return 0; + + double savedDist = inSavedDist; + KisVector2D end(pos2); + KisVector2D start(pos1); + + KisVector2D dragVec = end - start; + KisVector2D movement = dragVec; + + if (savedDist < 0) { + m_paintOp->paintAt(pos1, KisPaintInformation(pressure1, xTilt1, yTilt1, movement)); + savedDist = 0; + } + + double xSpacing = 0; + double ySpacing = 0; + + if ( m_varyBrushSpacingWithPressureWhenDrawingALine ) { + // XXX: The spacing should vary as the pressure changes along the + // line. + // This is a quick simplification. + xSpacing = m_brush->xSpacing((pressure1 + pressure2) / 2); + ySpacing = m_brush->ySpacing((pressure1 + pressure2) / 2); + } + else { + xSpacing = m_brush->xSpacing( PRESSURE_DEFAULT ); + ySpacing = m_brush->ySpacing( PRESSURE_DEFAULT ); + } + + if (xSpacing < 0.5) { + xSpacing = 0.5; + } + if (ySpacing < 0.5) { + ySpacing = 0.5; + } + + double xScale = 1; + double yScale = 1; + double spacing; + // Scale x or y so that we effectively have a square brush + // and calculate distance in that coordinate space. We reverse this scaling + // before drawing the brush. This produces the correct spacing in both + // x and y directions, even if the brush's aspect ratio is not 1:1. + if (xSpacing > ySpacing) { + yScale = xSpacing / ySpacing; + spacing = xSpacing; + } + else { + xScale = ySpacing / xSpacing; + spacing = ySpacing; + } + + dragVec.setX(dragVec.x() * xScale); + dragVec.setY(dragVec.y() * yScale); + + double newDist = dragVec.length(); + double dist = savedDist + newDist; + double l_savedDist = savedDist; + + if (dist < spacing) { + return dist; + } + + dragVec.normalize(); + KisVector2D step(0, 0); + + while (dist >= spacing) { + if (l_savedDist > 0) { + step += dragVec * (spacing - l_savedDist); + l_savedDist -= spacing; + } + else { + step += dragVec * spacing; + } + + KisPoint p(start.x() + (step.x() / xScale), start.y() + (step.y() / yScale)); + + double distanceMoved = step.length(); + double t = 0; + + if (newDist > DBL_EPSILON) { + t = distanceMoved / newDist; + } + + double pressure = (1 - t) * pressure1 + t * pressure2; + double xTilt = (1 - t) * xTilt1 + t * xTilt2; + double yTilt = (1 - t) * yTilt1 + t * yTilt2; + + m_paintOp->paintAt(p, KisPaintInformation(pressure, xTilt, yTilt, movement)); + dist -= spacing; + } + + if (dist > 0) + return dist; + else + return 0; +} + +void KisPainter::paintPolyline (const vKisPoint &points, + int index, int numPoints) +{ + if (index >= (int) points.count ()) + return; + + if (numPoints < 0) + numPoints = points.count (); + + if (index + numPoints > (int) points.count ()) + numPoints = points.count () - index; + + + for (int i = index; i < index + numPoints - 1; i++) + { + paintLine (points [index], 0/*pressure*/, 0, 0, points [index + 1], + 0/*pressure*/, 0, 0); + } +} + +void KisPainter::getBezierCurvePoints(const KisPoint &pos1, + const KisPoint &control1, + const KisPoint &control2, + const KisPoint &pos2, + vKisPoint& points) +{ + double d1 = pointToLineDistance(control1, pos1, pos2); + double d2 = pointToLineDistance(control2, pos1, pos2); + + if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) { + points.push_back(pos1); + } else { + // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 + KisVector2D p1 = pos1; + KisVector2D p2 = control1; + KisVector2D p3 = control2; + KisVector2D p4 = pos2; + + KisVector2D l2 = (p1 + p2) / 2; + KisVector2D h = (p2 + p3) / 2; + KisVector2D l3 = (l2 + h) / 2; + KisVector2D r3 = (p3 + p4) / 2; + KisVector2D r2 = (h + r3) / 2; + KisVector2D l4 = (l3 + r2) / 2; + KisVector2D r1 = l4; + KisVector2D l1 = p1; + KisVector2D r4 = p4; + + getBezierCurvePoints(l1.toKisPoint(), l2.toKisPoint(), l3.toKisPoint(), l4.toKisPoint(), points); + getBezierCurvePoints(r1.toKisPoint(), r2.toKisPoint(), r3.toKisPoint(), r4.toKisPoint(), points); + } +} + +double KisPainter::paintBezierCurve(const KisPoint &pos1, + const double pressure1, + const double xTilt1, + const double yTilt1, + const KisPoint &control1, + const KisPoint &control2, + const KisPoint &pos2, + const double pressure2, + const double xTilt2, + const double yTilt2, + const double savedDist) +{ + double newDistance; + double d1 = pointToLineDistance(control1, pos1, pos2); + double d2 = pointToLineDistance(control2, pos1, pos2); + + if (d1 < BEZIER_FLATNESS_THRESHOLD && d2 < BEZIER_FLATNESS_THRESHOLD) { + newDistance = paintLine(pos1, pressure1, xTilt1, yTilt1, pos2, pressure2, xTilt2, yTilt2, savedDist); + } else { + // Midpoint subdivision. See Foley & Van Dam Computer Graphics P.508 + KisVector2D p1 = pos1; + KisVector2D p2 = control1; + KisVector2D p3 = control2; + KisVector2D p4 = pos2; + + KisVector2D l2 = (p1 + p2) / 2; + KisVector2D h = (p2 + p3) / 2; + KisVector2D l3 = (l2 + h) / 2; + KisVector2D r3 = (p3 + p4) / 2; + KisVector2D r2 = (h + r3) / 2; + KisVector2D l4 = (l3 + r2) / 2; + KisVector2D r1 = l4; + KisVector2D l1 = p1; + KisVector2D r4 = p4; + + double midPressure = (pressure1 + pressure2) / 2; + double midXTilt = (xTilt1 + xTilt2) / 2; + double midYTilt = (yTilt1 + yTilt2) / 2; + + newDistance = paintBezierCurve(l1.toKisPoint(), pressure1, xTilt1, yTilt1, + l2.toKisPoint(), l3.toKisPoint(), + l4.toKisPoint(), midPressure, midXTilt, midYTilt, + savedDist); + newDistance = paintBezierCurve(r1.toKisPoint(), midPressure, midXTilt, midYTilt, + r2.toKisPoint(), + r3.toKisPoint(), + r4.toKisPoint(), pressure2, xTilt2, yTilt2, newDistance); + } + + return newDistance; +} + +void KisPainter::paintRect (const KisPoint &startPoint, + const KisPoint &endPoint, + const double /*pressure*/, + const double /*xTilt*/, + const double /*yTilt*/) +{ + KoRect normalizedRect = KisRect (startPoint, endPoint).normalize (); + + vKisPoint points; + + points.push_back(normalizedRect.topLeft()); + points.push_back(normalizedRect.bottomLeft()); + points.push_back(normalizedRect.bottomRight()); + points.push_back(normalizedRect.topRight()); + + paintPolygon(points); +} + +void KisPainter::paintEllipse (const KisPoint &startPoint, + const KisPoint &endPoint, + const double /*pressure*/, + const double /*xTilt*/, + const double /*yTilt*/) +{ + KisRect r = KisRect(startPoint, endPoint).normalize(); + + // See http://www.whizkidtech.redprince.net/bezier/circle/ for explanation. + // kappa = (4/3*(sqrt(2)-1)) + const double kappa = 0.5522847498; + const double lx = (r.width() / 2) * kappa; + const double ly = (r.height() / 2) * kappa; + + KisPoint center = r.center(); + + KisPoint p0(r.left(), center.y()); + KisPoint p1(r.left(), center.y() - ly); + KisPoint p2(center.x() - lx, r.top()); + KisPoint p3(center.x(), r.top()); + + vKisPoint points; + + getBezierCurvePoints(p0, p1, p2, p3, points); + + KisPoint p4(center.x() + lx, r.top()); + KisPoint p5(r.right(), center.y() - ly); + KisPoint p6(r.right(), center.y()); + + getBezierCurvePoints(p3, p4, p5, p6, points); + + KisPoint p7(r.right(), center.y() + ly); + KisPoint p8(center.x() + lx, r.bottom()); + KisPoint p9(center.x(), r.bottom()); + + getBezierCurvePoints(p6, p7, p8, p9, points); + + KisPoint p10(center.x() - lx, r.bottom()); + KisPoint p11(r.left(), center.y() + ly); + + getBezierCurvePoints(p9, p10, p11, p0, points); + + paintPolygon(points); +} + +void KisPainter::paintAt(const KisPoint & pos, + const double pressure, + const double xTilt, + const double yTilt) +{ + if (!m_paintOp) return; + m_paintOp->paintAt(pos, KisPaintInformation(pressure, xTilt, yTilt, KisVector2D())); +} + +double KisPainter::pointToLineDistance(const KisPoint& p, const KisPoint& l0, const KisPoint& l1) +{ + double lineLength = sqrt((l1.x() - l0.x()) * (l1.x() - l0.x()) + (l1.y() - l0.y()) * (l1.y() - l0.y())); + double distance = 0; + + if (lineLength > DBL_EPSILON) { + distance = ((l0.y() - l1.y()) * p.x() + (l1.x() - l0.x()) * p.y() + l0.x() * l1.y() - l1.x() * l0.y()) / lineLength; + distance = fabs(distance); + } + + return distance; +} + +/* + * Concave Polygon Scan Conversion + * by Paul Heckbert + * from "Graphics Gems", Academic Press, 1990 + */ + +/* + * concave: scan convert nvert-sided concave non-simple polygon with vertices at + * (point[i].x, point[i].y) for i in [0..nvert-1] within the window win by + * calling spanproc for each visible span of pixels. + * Polygon can be clockwise or counterclockwise. + * Algorithm does uniform point sampling at pixel centers. + * Inside-outside test done by Jordan's rule: a point is considered inside if + * an emanating ray intersects the polygon an odd number of times. + * drawproc should fill in pixels from xl to xr inclusive on scanline y, + * e.g: + * drawproc(y, xl, xr) + * int y, xl, xr; + * { + * int x; + * for (x=xl; x<=xr; x++) + * pixel_write(x, y, pixelvalue); + * } + * + * Paul Heckbert 30 June 81, 18 Dec 89 + */ + +typedef struct { /* a polygon edge */ + double x; /* x coordinate of edge's intersection with current scanline */ + double dx; /* change in x with respect to y */ + int i; /* edge number: edge i goes from pt[i] to pt[i+1] */ +} Edge; + +static int n; /* number of vertices */ +static const KisPoint *pt; /* vertices */ + +static int nact; /* number of active edges */ +static Edge *active; /* active edge list:edges crossing scanline y */ + +/* comparison routines for qsort */ +static int compare_ind(const void *pu, const void *pv) +{ + const int *u = static_cast(pu); + const int *v = static_cast(pv); + + return pt[*u].y() <= pt[*v].y() ? -1 : 1; +} + +static int compare_active(const void *pu, const void *pv) +{ + const Edge *u = static_cast(pu); + const Edge *v = static_cast(pv); + + return u->x <= v->x ? -1 : 1; +} + +static void cdelete(int i) /* remove edge i from active list */ +{ + int j; + + for (j=0; j=nact) return; /* edge not in active list; happens at win->y0*/ + nact--; + bcopy(&active[j+1], &active[j], (nact-j)*sizeof active[0]); +} + +static void cinsert(int i, int y) /* append edge i to end of active list */ +{ + int j; + double dx; + const KisPoint *p, *q; + + j = ix()-p->x())/(q->y()-p->y()); + active[nact].x = dx*(y+.5-p->y())+p->x(); + active[nact].i = i; + nact++; +} + +void KisPainter::fillPolygon(const vKisPoint& points, FillStyle fillStyle) +{ + int nvert = points.count(); + int k, y0, y1, y, i, j, xl, xr; + int *ind; /* list of vertex indices, sorted by pt[ind[j]].y */ + + n = nvert; + pt = &(points[0]); + if (n<3) return; + if (fillStyle == FillStyleNone) { + return; + } + + ind = new int[n]; + TQ_CHECK_PTR(ind); + active = new Edge[n]; + TQ_CHECK_PTR(active); + + /* create y-sorted array of indices ind[k] into vertex list */ + for (k=0; k(ceil(pt[ind[0]].y()-.5)); /* ymin of polygon */ + y1 = static_cast(floor(pt[ind[n-1]].y()-.5)); /* ymax of polygon */ + + int x0 = INT_MAX; + int x1 = INT_MIN; + + for (int i = 0; i < nvert; i++) { + int pointHighX = static_cast(ceil(points[i].x() - 0.5)); + int pointLowX = static_cast(floor(points[i].x() - 0.5)); + + if (pointLowX < x0) { + x0 = pointLowX; + } + if (pointHighX > x1) { + x1 = pointHighX; + } + } + + // Fill the polygon bounding rectangle with the required contents then we'll + // create a mask for the actual polygon coverage. + + KisPaintDeviceSP polygon = new KisPaintDevice(m_device->colorSpace(), "polygon"); + TQ_CHECK_PTR(polygon); + + KisFillPainter fillPainter(polygon); + TQRect boundingRectangle(x0, y0, x1 - x0 + 1, y1 - y0 + 1); + + // Clip to the image bounds. + if (m_device->image()) { + boundingRectangle &= m_device->image()->bounds(); + } + + switch (fillStyle) { + default: + // Fall through + case FillStyleGradient: + // Currently unsupported, fall through + case FillStyleStrokes: + // Currently unsupported, fall through + kdWarning(DBG_AREA_CORE) << "Unknown or unsupported fill style in fillPolygon\n"; + case FillStyleForegroundColor: + fillPainter.fillRect(boundingRectangle, paintColor(), OPACITY_OPAQUE); + break; + case FillStyleBackgroundColor: + fillPainter.fillRect(boundingRectangle, backgroundColor(), OPACITY_OPAQUE); + break; + case FillStylePattern: + Q_ASSERT(m_pattern != 0); + fillPainter.fillRect(boundingRectangle, m_pattern); + break; + } + + KisSelectionSP polygonMask = new KisSelection(polygon); + + for (y=y0; y<=y1; y++) { /* step through scanlines */ + /* scanline y is at y+.5 in continuous coordinates */ + + /* check vertices between previous scanline and current one, if any */ + for (; k0 ? i-1 : n-1; /* vertex previous to i */ + if (pt[j].y() <= y-.5) /* old edge, remove from active list */ + cdelete(j); + else if (pt[j].y() > y+.5) /* new edge, add to active list */ + cinsert(j, y); + j = i y+.5) /* new edge, add to active list */ + cinsert(i, y); + } + + /* sort active edge list by active[j].x */ + qsort(active, nact, sizeof active[0], compare_active); + + /* draw horizontal segments for scanline y */ + for (j=0; j(ceil(active[j].x-.5)); /* left end of span */ + xr = static_cast(floor(active[j+1].x-.5)); /* right end of span */ + + if (xl<=xr) { + KisHLineIterator it = polygonMask->createHLineIterator(xl, y, xr - xl + 1, true); + + while (!it.isDone()) { + // We're using a selection here, that means alpha colorspace, that means one byte. + it.rawData()[0] = MAX_SELECTED; + ++it; + } + } + + active[j].x += active[j].dx; /* increment edge coords */ + active[j+1].x += active[j+1].dx; + } + } + delete [] ind; + delete [] active; + + polygon->applySelectionMask(polygonMask); + + TQRect r = polygon->extent(); + + // The strokes for the outline may have already added updated the dirtyrect, but it can't hurt, + // and if we're painting without outlines, then there will be no dirty rect. Let's do it ourselves... + // addDirtyRect( r ); // XXX the bltSelection will add to the dirtyrect + + bltSelection(r.x(), r.y(), compositeOp(), polygon, opacity(), r.x(), r.y(), r.width(), r.height()); +} + +void KisPainter::paintPolygon(const vKisPoint& points) +{ + if (m_fillStyle != FillStyleNone) { + fillPolygon(points, m_fillStyle); + } + + if (m_strokeStyle != StrokeStyleNone) { + if (points.count() > 1) { + double distance = -1; + + for (uint i = 0; i < points.count() - 1; i++) { + distance = paintLine(points[i], PRESSURE_DEFAULT, 0, 0, points[i + 1], PRESSURE_DEFAULT, 0, 0, distance); + } + paintLine(points[points.count() - 1], PRESSURE_DEFAULT, 0, 0, points[0], PRESSURE_DEFAULT, 0, 0, distance); + } + } +} + diff --git a/chalk/core/kis_paintop.cc b/chalk/core/kis_paintop.cc deleted file mode 100644 index 59cfef6b..00000000 --- a/chalk/core/kis_paintop.cc +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * Copyright (c) 2004 Boudewijn Rempt - * Copyright (c) 2004 Clarence Dang - * Copyright (c) 2004 Adrian Page - * Copyright (c) 2004 Cyrille Berger - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "tqwidget.h" -#include "kis_painter.h" -#include "kis_layer.h" -#include "kis_types.h" -#include "kis_paintop.h" -#include "kis_alpha_mask.h" -#include "kis_point.h" -#include "kis_colorspace.h" -#include "kis_global.h" -#include "kis_iterators_pixel.h" -#include "kis_color.h" - -KisPaintOp::KisPaintOp(KisPainter * painter) - : m_dab(0) -{ - m_painter = painter; - setSource(painter->device()); -} - -KisPaintOp::~KisPaintOp() -{ -} - -KisPaintDeviceSP KisPaintOp::computeDab(KisAlphaMaskSP mask) { - return computeDab(mask, m_painter->device()->colorSpace()); -} - -KisPaintDeviceSP KisPaintOp::computeDab(KisAlphaMaskSP mask, KisColorSpace *cs) -{ - // XXX: According to the SeaShore source, the Gimp uses a - // temporary layer the size of the layer that is being painted - // on. This layer is cleared between painting actions. Our - // temporary layer, dab, is for every paintAt, composited with - // the target layer. We only use a real temporary layer for things - // like filter tools. - - if(!m_dab || m_dab->colorSpace() != cs) - m_dab = new KisPaintDevice(cs, "dab"); - TQ_CHECK_PTR(m_dab); - - KisColor kc = m_painter->paintColor(); - - KisColorSpace * colorSpace = m_dab->colorSpace(); - - TQ_INT32 pixelSize = colorSpace->pixelSize(); - - TQ_INT32 maskWidth = mask->width(); - TQ_INT32 maskHeight = mask->height(); - - // Convert the kiscolor to the right colorspace. - kc.convertTo(colorSpace); - - KisHLineIteratorPixel hiter = m_dab->createHLineIterator(0, 0, maskWidth, true); - for (int y = 0; y < maskHeight; y++) - { - int x=0; - while(! hiter.isDone()) - { - // XXX: Set mask - colorSpace->setAlpha(kc.data(), mask->alphaAt(x++, y), 1); - memcpy(hiter.rawData(), kc.data(), pixelSize); - ++hiter; - } - hiter.nextRow(); - } - - return m_dab; -} - -void KisPaintOp::splitCoordinate(double coordinate, TQ_INT32 *whole, double *fraction) -{ - TQ_INT32 i = static_cast(coordinate); - - if (coordinate < 0) { - // We always want the fractional part to be positive. - // E.g. -1.25 becomes -2 and +0.75 - i--; - } - - double f = coordinate - i; - - *whole = i; - *fraction = f; -} - -void KisPaintOp::setSource(KisPaintDeviceSP p) { - Q_ASSERT(p); - m_source = p; -} - - -KisPaintOpSettings* KisPaintOpFactory::settings(TQWidget* /*parent*/, const KisInputDevice& /*inputDevice*/) { return 0; } diff --git a/chalk/core/kis_paintop.cpp b/chalk/core/kis_paintop.cpp new file mode 100644 index 00000000..59cfef6b --- /dev/null +++ b/chalk/core/kis_paintop.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2002 Patrick Julien + * Copyright (c) 2004 Boudewijn Rempt + * Copyright (c) 2004 Clarence Dang + * Copyright (c) 2004 Adrian Page + * Copyright (c) 2004 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "tqwidget.h" +#include "kis_painter.h" +#include "kis_layer.h" +#include "kis_types.h" +#include "kis_paintop.h" +#include "kis_alpha_mask.h" +#include "kis_point.h" +#include "kis_colorspace.h" +#include "kis_global.h" +#include "kis_iterators_pixel.h" +#include "kis_color.h" + +KisPaintOp::KisPaintOp(KisPainter * painter) + : m_dab(0) +{ + m_painter = painter; + setSource(painter->device()); +} + +KisPaintOp::~KisPaintOp() +{ +} + +KisPaintDeviceSP KisPaintOp::computeDab(KisAlphaMaskSP mask) { + return computeDab(mask, m_painter->device()->colorSpace()); +} + +KisPaintDeviceSP KisPaintOp::computeDab(KisAlphaMaskSP mask, KisColorSpace *cs) +{ + // XXX: According to the SeaShore source, the Gimp uses a + // temporary layer the size of the layer that is being painted + // on. This layer is cleared between painting actions. Our + // temporary layer, dab, is for every paintAt, composited with + // the target layer. We only use a real temporary layer for things + // like filter tools. + + if(!m_dab || m_dab->colorSpace() != cs) + m_dab = new KisPaintDevice(cs, "dab"); + TQ_CHECK_PTR(m_dab); + + KisColor kc = m_painter->paintColor(); + + KisColorSpace * colorSpace = m_dab->colorSpace(); + + TQ_INT32 pixelSize = colorSpace->pixelSize(); + + TQ_INT32 maskWidth = mask->width(); + TQ_INT32 maskHeight = mask->height(); + + // Convert the kiscolor to the right colorspace. + kc.convertTo(colorSpace); + + KisHLineIteratorPixel hiter = m_dab->createHLineIterator(0, 0, maskWidth, true); + for (int y = 0; y < maskHeight; y++) + { + int x=0; + while(! hiter.isDone()) + { + // XXX: Set mask + colorSpace->setAlpha(kc.data(), mask->alphaAt(x++, y), 1); + memcpy(hiter.rawData(), kc.data(), pixelSize); + ++hiter; + } + hiter.nextRow(); + } + + return m_dab; +} + +void KisPaintOp::splitCoordinate(double coordinate, TQ_INT32 *whole, double *fraction) +{ + TQ_INT32 i = static_cast(coordinate); + + if (coordinate < 0) { + // We always want the fractional part to be positive. + // E.g. -1.25 becomes -2 and +0.75 + i--; + } + + double f = coordinate - i; + + *whole = i; + *fraction = f; +} + +void KisPaintOp::setSource(KisPaintDeviceSP p) { + Q_ASSERT(p); + m_source = p; +} + + +KisPaintOpSettings* KisPaintOpFactory::settings(TQWidget* /*parent*/, const KisInputDevice& /*inputDevice*/) { return 0; } diff --git a/chalk/core/kis_paintop_registry.cc b/chalk/core/kis_paintop_registry.cc deleted file mode 100644 index d549c3ad..00000000 --- a/chalk/core/kis_paintop_registry.cc +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2004 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "kis_generic_registry.h" -#include "kis_types.h" -#include "kis_paintop_registry.h" -#include "kis_paintop.h" -#include "kis_id.h" -#include "kis_debug_areas.h" -#include "kis_colorspace.h" - -KisPaintOpRegistry * KisPaintOpRegistry::m_singleton = 0; - -KisPaintOpRegistry::KisPaintOpRegistry() -{ - Q_ASSERT(KisPaintOpRegistry::m_singleton == 0); - KisPaintOpRegistry::m_singleton = this; - - TDETrader::OfferList offers = TDETrader::self()->query(TQString::fromLatin1("Chalk/Paintop"), - TQString::fromLatin1("(Type == 'Service') and " - "([X-Chalk-Version] == 2)")); - - TDETrader::OfferList::ConstIterator iter; - - for(iter = offers.begin(); iter != offers.end(); ++iter) - { - KService::Ptr service = *iter; - int errCode = 0; - KParts::Plugin* plugin = - KParts::ComponentFactory::createInstanceFromService ( service, this, 0, TQStringList(), &errCode); - if ( plugin ) - kdDebug(41006) << "found plugin " << service->property("Name").toString() << "\n"; - else { - kdDebug(41006) << "found plugin " << service->property("Name").toString() << ", " << errCode << "\n"; - if( errCode == KParts::ComponentFactory::ErrNoLibrary) - { - kdWarning(41006) << " Error loading plugin was : ErrNoLibrary " << KLibLoader::self()->lastErrorMessage() << endl; - } - } - - } - -} - -KisPaintOpRegistry::~KisPaintOpRegistry() -{ -} - -KisPaintOpRegistry* KisPaintOpRegistry::instance() -{ - if(KisPaintOpRegistry::m_singleton == 0) - { - KisPaintOpRegistry::m_singleton = new KisPaintOpRegistry(); - TQ_CHECK_PTR(KisPaintOpRegistry::m_singleton); - } - return KisPaintOpRegistry::m_singleton; -} - -KisPaintOp * KisPaintOpRegistry::paintOp(const KisID & id, const KisPaintOpSettings * settings, KisPainter * painter) const -{ - if (painter == 0) { - kdWarning() << " KisPaintOpRegistry::paintOp painter is null"; - return 0; - } - KisPaintOpFactorySP f = get(id); - if (f) { - return f->createOp(settings, painter); - } - else { - return 0; - } -} - -KisPaintOp * KisPaintOpRegistry::paintOp(const TQString & id, const KisPaintOpSettings * settings, KisPainter * painter) const -{ - return paintOp(KisID(id, ""), settings, painter); -} - -KisPaintOpSettings * KisPaintOpRegistry::settings(const KisID& id, TQWidget * parent, const KisInputDevice& inputDevice) const -{ - KisPaintOpFactory* f = get(id); - if (f) - return f->settings( parent, inputDevice ); - - return 0; -} - -bool KisPaintOpRegistry::userVisible(const KisID & id, KisColorSpace* cs) const -{ - - KisPaintOpFactorySP f = get(id); - if (!f) { - kdDebug(DBG_AREA_REGISTRY) << "No paintop " << id.id() << "\n"; - return false; - } - return f->userVisible(cs); - -} - -TQString KisPaintOpRegistry::pixmap(const KisID & id) const -{ - KisPaintOpFactorySP f = get(id); - - if (!f) { - kdDebug(DBG_AREA_REGISTRY) << "No paintop " << id.id() << "\n"; - return ""; - } - - return f->pixmap(); -} - -#include "kis_paintop_registry.moc" diff --git a/chalk/core/kis_paintop_registry.cpp b/chalk/core/kis_paintop_registry.cpp new file mode 100644 index 00000000..d549c3ad --- /dev/null +++ b/chalk/core/kis_paintop_registry.cpp @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kis_generic_registry.h" +#include "kis_types.h" +#include "kis_paintop_registry.h" +#include "kis_paintop.h" +#include "kis_id.h" +#include "kis_debug_areas.h" +#include "kis_colorspace.h" + +KisPaintOpRegistry * KisPaintOpRegistry::m_singleton = 0; + +KisPaintOpRegistry::KisPaintOpRegistry() +{ + Q_ASSERT(KisPaintOpRegistry::m_singleton == 0); + KisPaintOpRegistry::m_singleton = this; + + TDETrader::OfferList offers = TDETrader::self()->query(TQString::fromLatin1("Chalk/Paintop"), + TQString::fromLatin1("(Type == 'Service') and " + "([X-Chalk-Version] == 2)")); + + TDETrader::OfferList::ConstIterator iter; + + for(iter = offers.begin(); iter != offers.end(); ++iter) + { + KService::Ptr service = *iter; + int errCode = 0; + KParts::Plugin* plugin = + KParts::ComponentFactory::createInstanceFromService ( service, this, 0, TQStringList(), &errCode); + if ( plugin ) + kdDebug(41006) << "found plugin " << service->property("Name").toString() << "\n"; + else { + kdDebug(41006) << "found plugin " << service->property("Name").toString() << ", " << errCode << "\n"; + if( errCode == KParts::ComponentFactory::ErrNoLibrary) + { + kdWarning(41006) << " Error loading plugin was : ErrNoLibrary " << KLibLoader::self()->lastErrorMessage() << endl; + } + } + + } + +} + +KisPaintOpRegistry::~KisPaintOpRegistry() +{ +} + +KisPaintOpRegistry* KisPaintOpRegistry::instance() +{ + if(KisPaintOpRegistry::m_singleton == 0) + { + KisPaintOpRegistry::m_singleton = new KisPaintOpRegistry(); + TQ_CHECK_PTR(KisPaintOpRegistry::m_singleton); + } + return KisPaintOpRegistry::m_singleton; +} + +KisPaintOp * KisPaintOpRegistry::paintOp(const KisID & id, const KisPaintOpSettings * settings, KisPainter * painter) const +{ + if (painter == 0) { + kdWarning() << " KisPaintOpRegistry::paintOp painter is null"; + return 0; + } + KisPaintOpFactorySP f = get(id); + if (f) { + return f->createOp(settings, painter); + } + else { + return 0; + } +} + +KisPaintOp * KisPaintOpRegistry::paintOp(const TQString & id, const KisPaintOpSettings * settings, KisPainter * painter) const +{ + return paintOp(KisID(id, ""), settings, painter); +} + +KisPaintOpSettings * KisPaintOpRegistry::settings(const KisID& id, TQWidget * parent, const KisInputDevice& inputDevice) const +{ + KisPaintOpFactory* f = get(id); + if (f) + return f->settings( parent, inputDevice ); + + return 0; +} + +bool KisPaintOpRegistry::userVisible(const KisID & id, KisColorSpace* cs) const +{ + + KisPaintOpFactorySP f = get(id); + if (!f) { + kdDebug(DBG_AREA_REGISTRY) << "No paintop " << id.id() << "\n"; + return false; + } + return f->userVisible(cs); + +} + +TQString KisPaintOpRegistry::pixmap(const KisID & id) const +{ + KisPaintOpFactorySP f = get(id); + + if (!f) { + kdDebug(DBG_AREA_REGISTRY) << "No paintop " << id.id() << "\n"; + return ""; + } + + return f->pixmap(); +} + +#include "kis_paintop_registry.moc" diff --git a/chalk/core/kis_palette.cc b/chalk/core/kis_palette.cc deleted file mode 100644 index aa62b07a..00000000 --- a/chalk/core/kis_palette.cc +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2005 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#ifdef HAVE_SYS_TYPES_H -#include -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include "kis_debug_areas.h" -#include "kis_palette.h" -#include "kis_iterators_pixel.h" - - -namespace { - enum enumPaletteType { - FORMAT_UNKNOWN, - FORMAT_GPL, // Gimp palette - FORMAT_PAL, // RIFF palette - FORMAT_ACT // Photoshop binary color palette - }; - -} - - -KisPalette::KisPalette(const TQImage * img, TQ_INT32 nColors, const TQString & name) - : super(TQString("")), - m_name(name) -{ - Q_ASSERT(nColors > 0); - Q_ASSERT(!img->isNull()); - - // XXX: Implement - - m_columns = 0; // Set the default value that the GIMP uses... -} - -KisPalette::KisPalette(const KisPaintDeviceSP device, TQ_INT32 nColors, const TQString & name) - : super(TQString("")), - m_name(name) -{ - Q_ASSERT(nColors > 0); - Q_ASSERT(device != 0); - - - // XXX: Implement - m_columns = 0; // Set the default value that the GIMP uses... -} - - -KisPalette::KisPalette(const KisGradient * gradient, TQ_INT32 nColors, const TQString & name) - : super(TQString("")), - m_name(name) -{ - Q_ASSERT(nColors > 0); - Q_ASSERT(gradient != 0); - - double dx, cur_x; - TQColor c; - TQ_INT32 i; - TQ_UINT8 opacity; - dx = 1.0 / (nColors - 1); - - KisPaletteEntry e; - for (i = 0, cur_x = 0; i < nColors; i++, cur_x += dx) { - gradient->colorAt(cur_x, &e.color, &opacity); - e.name = "Untitled"; - add(e); - } - - m_columns = 0; // Set the default value that the GIMP uses... -} - -KisPalette::KisPalette(const TQString& filename) - : super(filename) -{ - // Implemented in super class - m_columns = 0; // Set the default value that the GIMP uses... -} - -KisPalette::KisPalette() - : super("") -{ - m_columns = 0; // Set the default value that the GIMP uses... -} - -/// Create an copied palette -KisPalette::KisPalette(const KisPalette& rhs) - : super("") -{ - setFilename(rhs.filename()); - m_ownData = false; - m_img = rhs.m_img; - m_name = rhs.m_name; - m_comment = rhs.m_comment; - m_columns = rhs.m_columns; - m_colors = rhs.m_colors; - setValid(true); -} - -KisPalette::~KisPalette() -{ -} - -bool KisPalette::load() -{ - TQFile file(filename()); - file.open(IO_ReadOnly); - m_data = file.readAll(); - file.close(); - return init(); -} - - -bool KisPalette::save() -{ - TQFile file(filename()); - if (!file.open(IO_WriteOnly | IO_Truncate)) { - return false; - } - - TQTextStream stream(&file); - // Header: Magic\nName: \nColumns: - // In any case, we don't use Columns... - stream << "GIMP Palette\nName: " << name() << "\nColumns: " << m_columns << "\n#\n"; - - for (uint i = 0; i < m_colors.size(); i++) { - const KisPaletteEntry& entry = m_colors.at(i); - TQColor c = entry.color; - stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; - if (entry.name.isEmpty()) - stream << "Untitled\n"; - else - stream << entry.name << "\n"; - } - - file.close(); - return true; -} - -TQImage KisPalette::img() -{ - return m_img; -} - -TQ_INT32 KisPalette::nColors() -{ - return m_colors.count(); -} - -bool KisPalette::init() -{ - enumPaletteType format = FORMAT_UNKNOWN; - - TQString s = TQString::fromUtf8(m_data.data(), m_data.count()); - - if (s.isEmpty() || s.isNull() || s.length() < 50) { - kdWarning(DBG_AREA_FILE) << "Illegal Gimp palette file: " << filename() << "\n"; - return false; - } - - - if (s.startsWith("RIFF") || s.startsWith("PAL data")) - { - format = FORMAT_PAL; - } - else if (s.startsWith("GIMP Palette")) - { - // XXX: No checks for wrong input yet! - TQ_UINT32 index = 0; - - TQStringList lines = TQStringList::split("\n", s); - - if (lines.size() < 3) { - return false; - } - - TQString entry, channel, columns; - TQStringList c; - TQ_INT32 r, g, b; - TQColor color; - KisPaletteEntry e; - - format = FORMAT_GPL; - - // Read name - if (!lines[1].startsWith("Name: ") || !lines[0].startsWith("GIMP") ) - { - kdWarning(DBG_AREA_FILE) << "Illegal Gimp palette file: " << filename() << "\n"; - return false; - } - - setName(i18n(lines[1].mid(strlen("Name: ")).stripWhiteSpace().ascii())); - - index = 2; - - // Read columns - if (lines[index].startsWith("Columns: ")) { - columns = lines[index].mid(strlen("Columns: ")).stripWhiteSpace();; - m_columns = columns.toInt(); - index = 3; - } - - for (TQ_UINT32 i = index; i < lines.size(); i++) { - if (lines[i].startsWith("#")) { - m_comment += lines[i].mid(1).stripWhiteSpace() + " "; - } - else if (!lines[i].isEmpty()) - { - TQStringList a = TQStringList::split(" ", lines[i].replace(TQChar('\t'), " ")); - - if (a.count() < 3) - { - break; - } - - r = a[0].toInt(); - a.pop_front(); - g = a[0].toInt(); - a.pop_front(); - b = a[0].toInt(); - a.pop_front(); - - if (r < 0 || r > 255 || - g < 0 || g > 255 || - b < 0 || b > 255) - { - break; - } - - color = TQColor(r, g, b); - e.color = color; - - TQString name = a.join(" "); - e.name = name.isEmpty() ? i18n("Untitled") : name; - - add(e); - } - } - setValid(true); - return true; - } - else if (s.length() == 768) { - kdWarning(DBG_AREA_FILE) << "Photoshop format palette file. Not implemented yet\n"; - format = FORMAT_ACT; - } - return false; -} - - -void KisPalette::add(const KisPaletteEntry & c) -{ - m_colors.push_back(c); -} - -void KisPalette::remove(const KisPaletteEntry & c) -{ - TQValueVector::iterator it = m_colors.begin(); - TQValueVector::iterator end = m_colors.end(); - - while (it != end) { - if ((*it) == c) { - m_colors.erase(it); - return; - } - ++it; - } -} - -KisPaletteEntry KisPalette::getColor(TQ_UINT32 index) -{ - return m_colors[index]; -} - -#include "kis_palette.moc" diff --git a/chalk/core/kis_palette.cpp b/chalk/core/kis_palette.cpp new file mode 100644 index 00000000..aa62b07a --- /dev/null +++ b/chalk/core/kis_palette.cpp @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2005 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "kis_debug_areas.h" +#include "kis_palette.h" +#include "kis_iterators_pixel.h" + + +namespace { + enum enumPaletteType { + FORMAT_UNKNOWN, + FORMAT_GPL, // Gimp palette + FORMAT_PAL, // RIFF palette + FORMAT_ACT // Photoshop binary color palette + }; + +} + + +KisPalette::KisPalette(const TQImage * img, TQ_INT32 nColors, const TQString & name) + : super(TQString("")), + m_name(name) +{ + Q_ASSERT(nColors > 0); + Q_ASSERT(!img->isNull()); + + // XXX: Implement + + m_columns = 0; // Set the default value that the GIMP uses... +} + +KisPalette::KisPalette(const KisPaintDeviceSP device, TQ_INT32 nColors, const TQString & name) + : super(TQString("")), + m_name(name) +{ + Q_ASSERT(nColors > 0); + Q_ASSERT(device != 0); + + + // XXX: Implement + m_columns = 0; // Set the default value that the GIMP uses... +} + + +KisPalette::KisPalette(const KisGradient * gradient, TQ_INT32 nColors, const TQString & name) + : super(TQString("")), + m_name(name) +{ + Q_ASSERT(nColors > 0); + Q_ASSERT(gradient != 0); + + double dx, cur_x; + TQColor c; + TQ_INT32 i; + TQ_UINT8 opacity; + dx = 1.0 / (nColors - 1); + + KisPaletteEntry e; + for (i = 0, cur_x = 0; i < nColors; i++, cur_x += dx) { + gradient->colorAt(cur_x, &e.color, &opacity); + e.name = "Untitled"; + add(e); + } + + m_columns = 0; // Set the default value that the GIMP uses... +} + +KisPalette::KisPalette(const TQString& filename) + : super(filename) +{ + // Implemented in super class + m_columns = 0; // Set the default value that the GIMP uses... +} + +KisPalette::KisPalette() + : super("") +{ + m_columns = 0; // Set the default value that the GIMP uses... +} + +/// Create an copied palette +KisPalette::KisPalette(const KisPalette& rhs) + : super("") +{ + setFilename(rhs.filename()); + m_ownData = false; + m_img = rhs.m_img; + m_name = rhs.m_name; + m_comment = rhs.m_comment; + m_columns = rhs.m_columns; + m_colors = rhs.m_colors; + setValid(true); +} + +KisPalette::~KisPalette() +{ +} + +bool KisPalette::load() +{ + TQFile file(filename()); + file.open(IO_ReadOnly); + m_data = file.readAll(); + file.close(); + return init(); +} + + +bool KisPalette::save() +{ + TQFile file(filename()); + if (!file.open(IO_WriteOnly | IO_Truncate)) { + return false; + } + + TQTextStream stream(&file); + // Header: Magic\nName: \nColumns: + // In any case, we don't use Columns... + stream << "GIMP Palette\nName: " << name() << "\nColumns: " << m_columns << "\n#\n"; + + for (uint i = 0; i < m_colors.size(); i++) { + const KisPaletteEntry& entry = m_colors.at(i); + TQColor c = entry.color; + stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; + if (entry.name.isEmpty()) + stream << "Untitled\n"; + else + stream << entry.name << "\n"; + } + + file.close(); + return true; +} + +TQImage KisPalette::img() +{ + return m_img; +} + +TQ_INT32 KisPalette::nColors() +{ + return m_colors.count(); +} + +bool KisPalette::init() +{ + enumPaletteType format = FORMAT_UNKNOWN; + + TQString s = TQString::fromUtf8(m_data.data(), m_data.count()); + + if (s.isEmpty() || s.isNull() || s.length() < 50) { + kdWarning(DBG_AREA_FILE) << "Illegal Gimp palette file: " << filename() << "\n"; + return false; + } + + + if (s.startsWith("RIFF") || s.startsWith("PAL data")) + { + format = FORMAT_PAL; + } + else if (s.startsWith("GIMP Palette")) + { + // XXX: No checks for wrong input yet! + TQ_UINT32 index = 0; + + TQStringList lines = TQStringList::split("\n", s); + + if (lines.size() < 3) { + return false; + } + + TQString entry, channel, columns; + TQStringList c; + TQ_INT32 r, g, b; + TQColor color; + KisPaletteEntry e; + + format = FORMAT_GPL; + + // Read name + if (!lines[1].startsWith("Name: ") || !lines[0].startsWith("GIMP") ) + { + kdWarning(DBG_AREA_FILE) << "Illegal Gimp palette file: " << filename() << "\n"; + return false; + } + + setName(i18n(lines[1].mid(strlen("Name: ")).stripWhiteSpace().ascii())); + + index = 2; + + // Read columns + if (lines[index].startsWith("Columns: ")) { + columns = lines[index].mid(strlen("Columns: ")).stripWhiteSpace();; + m_columns = columns.toInt(); + index = 3; + } + + for (TQ_UINT32 i = index; i < lines.size(); i++) { + if (lines[i].startsWith("#")) { + m_comment += lines[i].mid(1).stripWhiteSpace() + " "; + } + else if (!lines[i].isEmpty()) + { + TQStringList a = TQStringList::split(" ", lines[i].replace(TQChar('\t'), " ")); + + if (a.count() < 3) + { + break; + } + + r = a[0].toInt(); + a.pop_front(); + g = a[0].toInt(); + a.pop_front(); + b = a[0].toInt(); + a.pop_front(); + + if (r < 0 || r > 255 || + g < 0 || g > 255 || + b < 0 || b > 255) + { + break; + } + + color = TQColor(r, g, b); + e.color = color; + + TQString name = a.join(" "); + e.name = name.isEmpty() ? i18n("Untitled") : name; + + add(e); + } + } + setValid(true); + return true; + } + else if (s.length() == 768) { + kdWarning(DBG_AREA_FILE) << "Photoshop format palette file. Not implemented yet\n"; + format = FORMAT_ACT; + } + return false; +} + + +void KisPalette::add(const KisPaletteEntry & c) +{ + m_colors.push_back(c); +} + +void KisPalette::remove(const KisPaletteEntry & c) +{ + TQValueVector::iterator it = m_colors.begin(); + TQValueVector::iterator end = m_colors.end(); + + while (it != end) { + if ((*it) == c) { + m_colors.erase(it); + return; + } + ++it; + } +} + +KisPaletteEntry KisPalette::getColor(TQ_UINT32 index) +{ + return m_colors[index]; +} + +#include "kis_palette.moc" diff --git a/chalk/core/kis_pattern.cc b/chalk/core/kis_pattern.cc deleted file mode 100644 index 654449b8..00000000 --- a/chalk/core/kis_pattern.cc +++ /dev/null @@ -1,335 +0,0 @@ -/* - * kis_pattern.cc - part of Krayon - * - * Copyright (c) 2000 Matthias Elter - * 2001 John Califf - * 2004 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_pattern.h" - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "kis_color.h" -#include "kis_layer.h" -#include "kis_paint_device.h" - -namespace { - struct GimpPatternHeader { - TQ_UINT32 header_size; /* header_size = sizeof (PatternHeader) + brush name */ - TQ_UINT32 version; /* pattern file version # */ - TQ_UINT32 width; /* width of pattern */ - TQ_UINT32 height; /* height of pattern */ - TQ_UINT32 bytes; /* depth of pattern in bytes : 1, 2, 3 or 4*/ - TQ_UINT32 magic_number; /* GIMP brush magic number */ - }; - - // Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT' - TQ_UINT32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0)); -} - -KisPattern::KisPattern(const TQString& file) : super(file), m_hasFile(true) -{ -} - -KisPattern::KisPattern(KisPaintDevice* image, int x, int y, int w, int h) - : super(""), m_hasFile(false) -{ - // Forcefully convert to RGBA8 - // XXX profile and exposure? - setImage(image->convertToTQImage(0, x, y, w, h)); - setName(image->name()); -} - -KisPattern::~KisPattern() -{ -} - -bool KisPattern::load() -{ - if (!m_hasFile) - return true; - - TQFile file(filename()); - file.open(IO_ReadOnly); - TQByteArray data = file.readAll(); - if (!data.isEmpty()) { - TQ_INT32 startPos = m_data.size(); - - m_data.resize(m_data.size() + data.count()); - memcpy(&m_data[startPos], data.data(), data.count()); - } - file.close(); - return init(); -} - -bool KisPattern::save() -{ - TQFile file(filename()); - file.open(IO_WriteOnly | IO_Truncate); - - TQTextStream stream(&file); - // Header: header_size (24+name length),version,width,height,colourdepth of brush,magic,name - // depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA - // magic = "GPAT", as a single uint32, the docs are wrong here! - // name is UTF-8 (\0-terminated! The docs say nothing about this!) - // _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!) - // We only save RGBA at the moment - // Version is 1 for now... - - GimpPatternHeader ph; - TQCString utf8Name = name().utf8(); - char const* name = utf8Name.data(); - int nameLength = tqstrlen(name); - - ph.header_size = htonl(sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0 - ph.version = htonl(1); - ph.width = htonl(width()); - ph.height = htonl(height()); - ph.bytes = htonl(4); - ph.magic_number = htonl(GimpPatternMagic); - - TQByteArray bytes; - bytes.setRawData(reinterpret_cast(&ph), sizeof(GimpPatternHeader)); - int wrote = file.writeBlock(bytes); - bytes.resetRawData(reinterpret_cast(&ph), sizeof(GimpPatternHeader)); - - if (wrote == -1) - return false; - - wrote = file.writeBlock(name, nameLength + 1); // Trailing 0 apparantly! - if (wrote == -1) - return false; - - int k = 0; - bytes.resize(width() * height() * 4); - for (TQ_INT32 y = 0; y < height(); y++) { - for (TQ_INT32 x = 0; x < width(); x++) { - // RGBA only - TQRgb pixel = m_img.pixel(x,y); - bytes[k++] = static_cast(tqRed(pixel)); - bytes[k++] = static_cast(tqGreen(pixel)); - bytes[k++] = static_cast(tqBlue(pixel)); - bytes[k++] = static_cast(tqAlpha(pixel)); - } - } - - wrote = file.writeBlock(bytes); - if (wrote == -1) - return false; - - file.close(); - - return true; -} - -TQImage KisPattern::img() -{ - return m_img; -} - -bool KisPattern::init() -{ - // load Gimp patterns - GimpPatternHeader bh; - TQ_INT32 k; - TQValueVector name; - - if (sizeof(GimpPatternHeader) > m_data.size()) { - return false; - } - - memcpy(&bh, &m_data[0], sizeof(GimpPatternHeader)); - bh.header_size = ntohl(bh.header_size); - bh.version = ntohl(bh.version); - bh.width = ntohl(bh.width); - bh.height = ntohl(bh.height); - bh.bytes = ntohl(bh.bytes); - bh.magic_number = ntohl(bh.magic_number); - - if (bh.header_size > m_data.size() || bh.header_size == 0) { - return false; - } - - name.resize(bh.header_size - sizeof(GimpPatternHeader)); - memcpy(&name[0], &m_data[sizeof(GimpPatternHeader)], name.size()); - - if (name[name.size() - 1]) { - return false; - } - - setName(i18n(&name[0])); - - if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) { - return false; - } - - k = bh.header_size; - - if (bh.bytes == 1) { - // Grayscale - TQ_INT32 val; - - for (TQ_UINT32 y = 0; y < bh.height; y++) { - for (TQ_UINT32 x = 0; x < bh.width; x++, k++) { - if (static_cast(k) > m_data.size()) { - kdDebug(DBG_AREA_FILE) << "failed in gray\n"; - return false; - } - - val = m_data[k]; - m_img.setPixel(x, y, tqRgb(val, val, val)); - m_img.setAlphaBuffer(false); - } - } - } else if (bh.bytes == 2) { - // Grayscale + A - TQ_INT32 val; - TQ_INT32 alpha; - for (TQ_UINT32 y = 0; y < bh.height; y++) { - for (TQ_UINT32 x = 0; x < bh.width; x++, k++) { - if (static_cast(k + 2) > m_data.size()) { - kdDebug(DBG_AREA_FILE) << "failed in grayA\n"; - return false; - } - - val = m_data[k]; - alpha = m_data[k++]; - m_img.setPixel(x, y, tqRgba(val, val, val, alpha)); - m_img.setAlphaBuffer(true); - } - } - } else if (bh.bytes == 3) { - // RGB without alpha - for (TQ_UINT32 y = 0; y < bh.height; y++) { - for (TQ_UINT32 x = 0; x < bh.width; x++) { - if (static_cast(k + 3) > m_data.size()) { - kdDebug(DBG_AREA_FILE) << "failed in RGB\n"; - return false; - } - - m_img.setPixel(x, y, tqRgb(m_data[k], - m_data[k + 1], - m_data[k + 2])); - k += 3; - m_img.setAlphaBuffer(false); - } - } - } else if (bh.bytes == 4) { - // Has alpha - for (TQ_UINT32 y = 0; y < bh.height; y++) { - for (TQ_UINT32 x = 0; x < bh.width; x++) { - if (static_cast(k + 4) > m_data.size()) { - kdDebug(DBG_AREA_FILE) << "failed in RGBA\n"; - return false; - } - - m_img.setPixel(x, y, tqRgba(m_data[k], - m_data[k + 1], - m_data[k + 2], - m_data[k + 3])); - k += 4; - m_img.setAlphaBuffer(true); - } - } - } else { - return false; - } - - if (m_img.isNull()) { - return false; - } - - setWidth(m_img.width()); - setHeight(m_img.height()); - - setValid(true); - - return true; -} - -KisPaintDeviceSP KisPattern::image(KisColorSpace * colorSpace) { - // Check if there's already a pattern prepared for this colorspace - TQMap::const_iterator it = m_colorspaces.find(colorSpace->id().id()); - if (it != m_colorspaces.end()) - return (*it); - - // If not, create one - KisPaintDeviceSP layer = new KisPaintDevice(colorSpace, "pattern"); - - TQ_CHECK_PTR(layer); - - layer->convertFromTQImage(m_img,""); - - m_colorspaces[colorSpace->id().id()] = layer; - return layer; -} - -TQ_INT32 KisPattern::width() const -{ - return m_width; -} - -void KisPattern::setWidth(TQ_INT32 w) -{ - m_width = w; -} - -TQ_INT32 KisPattern::height() const -{ - return m_height; -} - -void KisPattern::setHeight(TQ_INT32 h) -{ - m_height = h; -} - -void KisPattern::setImage(const TQImage& img) -{ - m_hasFile = false; - m_img = img; - m_img.detach(); - - setWidth(img.width()); - setHeight(img.height()); - - setValid(true); -} - -KisPattern* KisPattern::clone() const -{ - KisPattern* pattern = new KisPattern(""); - pattern->setImage(m_img); - pattern->setName(name()); - return pattern; -} - -#include "kis_pattern.moc" diff --git a/chalk/core/kis_pattern.cpp b/chalk/core/kis_pattern.cpp new file mode 100644 index 00000000..b2a52995 --- /dev/null +++ b/chalk/core/kis_pattern.cpp @@ -0,0 +1,335 @@ +/* + * kis_pattern.cpp - part of Krayon + * + * Copyright (c) 2000 Matthias Elter + * 2001 John Califf + * 2004 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_pattern.h" + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "kis_color.h" +#include "kis_layer.h" +#include "kis_paint_device.h" + +namespace { + struct GimpPatternHeader { + TQ_UINT32 header_size; /* header_size = sizeof (PatternHeader) + brush name */ + TQ_UINT32 version; /* pattern file version # */ + TQ_UINT32 width; /* width of pattern */ + TQ_UINT32 height; /* height of pattern */ + TQ_UINT32 bytes; /* depth of pattern in bytes : 1, 2, 3 or 4*/ + TQ_UINT32 magic_number; /* GIMP brush magic number */ + }; + + // Yes! This is _NOT_ what my pat.txt file says. It's really not 'GIMP', but 'GPAT' + TQ_UINT32 const GimpPatternMagic = (('G' << 24) + ('P' << 16) + ('A' << 8) + ('T' << 0)); +} + +KisPattern::KisPattern(const TQString& file) : super(file), m_hasFile(true) +{ +} + +KisPattern::KisPattern(KisPaintDevice* image, int x, int y, int w, int h) + : super(""), m_hasFile(false) +{ + // Forcefully convert to RGBA8 + // XXX profile and exposure? + setImage(image->convertToTQImage(0, x, y, w, h)); + setName(image->name()); +} + +KisPattern::~KisPattern() +{ +} + +bool KisPattern::load() +{ + if (!m_hasFile) + return true; + + TQFile file(filename()); + file.open(IO_ReadOnly); + TQByteArray data = file.readAll(); + if (!data.isEmpty()) { + TQ_INT32 startPos = m_data.size(); + + m_data.resize(m_data.size() + data.count()); + memcpy(&m_data[startPos], data.data(), data.count()); + } + file.close(); + return init(); +} + +bool KisPattern::save() +{ + TQFile file(filename()); + file.open(IO_WriteOnly | IO_Truncate); + + TQTextStream stream(&file); + // Header: header_size (24+name length),version,width,height,colourdepth of brush,magic,name + // depth: 1 = greyscale, 2 = greyscale + A, 3 = RGB, 4 = RGBA + // magic = "GPAT", as a single uint32, the docs are wrong here! + // name is UTF-8 (\0-terminated! The docs say nothing about this!) + // _All_ data in network order, it seems! (not mentioned in gimp-2.2.8/devel-docs/pat.txt!!) + // We only save RGBA at the moment + // Version is 1 for now... + + GimpPatternHeader ph; + TQCString utf8Name = name().utf8(); + char const* name = utf8Name.data(); + int nameLength = tqstrlen(name); + + ph.header_size = htonl(sizeof(GimpPatternHeader) + nameLength + 1); // trailing 0 + ph.version = htonl(1); + ph.width = htonl(width()); + ph.height = htonl(height()); + ph.bytes = htonl(4); + ph.magic_number = htonl(GimpPatternMagic); + + TQByteArray bytes; + bytes.setRawData(reinterpret_cast(&ph), sizeof(GimpPatternHeader)); + int wrote = file.writeBlock(bytes); + bytes.resetRawData(reinterpret_cast(&ph), sizeof(GimpPatternHeader)); + + if (wrote == -1) + return false; + + wrote = file.writeBlock(name, nameLength + 1); // Trailing 0 apparantly! + if (wrote == -1) + return false; + + int k = 0; + bytes.resize(width() * height() * 4); + for (TQ_INT32 y = 0; y < height(); y++) { + for (TQ_INT32 x = 0; x < width(); x++) { + // RGBA only + TQRgb pixel = m_img.pixel(x,y); + bytes[k++] = static_cast(tqRed(pixel)); + bytes[k++] = static_cast(tqGreen(pixel)); + bytes[k++] = static_cast(tqBlue(pixel)); + bytes[k++] = static_cast(tqAlpha(pixel)); + } + } + + wrote = file.writeBlock(bytes); + if (wrote == -1) + return false; + + file.close(); + + return true; +} + +TQImage KisPattern::img() +{ + return m_img; +} + +bool KisPattern::init() +{ + // load Gimp patterns + GimpPatternHeader bh; + TQ_INT32 k; + TQValueVector name; + + if (sizeof(GimpPatternHeader) > m_data.size()) { + return false; + } + + memcpy(&bh, &m_data[0], sizeof(GimpPatternHeader)); + bh.header_size = ntohl(bh.header_size); + bh.version = ntohl(bh.version); + bh.width = ntohl(bh.width); + bh.height = ntohl(bh.height); + bh.bytes = ntohl(bh.bytes); + bh.magic_number = ntohl(bh.magic_number); + + if (bh.header_size > m_data.size() || bh.header_size == 0) { + return false; + } + + name.resize(bh.header_size - sizeof(GimpPatternHeader)); + memcpy(&name[0], &m_data[sizeof(GimpPatternHeader)], name.size()); + + if (name[name.size() - 1]) { + return false; + } + + setName(i18n(&name[0])); + + if (bh.width == 0 || bh.height == 0 || !m_img.create(bh.width, bh.height, 32)) { + return false; + } + + k = bh.header_size; + + if (bh.bytes == 1) { + // Grayscale + TQ_INT32 val; + + for (TQ_UINT32 y = 0; y < bh.height; y++) { + for (TQ_UINT32 x = 0; x < bh.width; x++, k++) { + if (static_cast(k) > m_data.size()) { + kdDebug(DBG_AREA_FILE) << "failed in gray\n"; + return false; + } + + val = m_data[k]; + m_img.setPixel(x, y, tqRgb(val, val, val)); + m_img.setAlphaBuffer(false); + } + } + } else if (bh.bytes == 2) { + // Grayscale + A + TQ_INT32 val; + TQ_INT32 alpha; + for (TQ_UINT32 y = 0; y < bh.height; y++) { + for (TQ_UINT32 x = 0; x < bh.width; x++, k++) { + if (static_cast(k + 2) > m_data.size()) { + kdDebug(DBG_AREA_FILE) << "failed in grayA\n"; + return false; + } + + val = m_data[k]; + alpha = m_data[k++]; + m_img.setPixel(x, y, tqRgba(val, val, val, alpha)); + m_img.setAlphaBuffer(true); + } + } + } else if (bh.bytes == 3) { + // RGB without alpha + for (TQ_UINT32 y = 0; y < bh.height; y++) { + for (TQ_UINT32 x = 0; x < bh.width; x++) { + if (static_cast(k + 3) > m_data.size()) { + kdDebug(DBG_AREA_FILE) << "failed in RGB\n"; + return false; + } + + m_img.setPixel(x, y, tqRgb(m_data[k], + m_data[k + 1], + m_data[k + 2])); + k += 3; + m_img.setAlphaBuffer(false); + } + } + } else if (bh.bytes == 4) { + // Has alpha + for (TQ_UINT32 y = 0; y < bh.height; y++) { + for (TQ_UINT32 x = 0; x < bh.width; x++) { + if (static_cast(k + 4) > m_data.size()) { + kdDebug(DBG_AREA_FILE) << "failed in RGBA\n"; + return false; + } + + m_img.setPixel(x, y, tqRgba(m_data[k], + m_data[k + 1], + m_data[k + 2], + m_data[k + 3])); + k += 4; + m_img.setAlphaBuffer(true); + } + } + } else { + return false; + } + + if (m_img.isNull()) { + return false; + } + + setWidth(m_img.width()); + setHeight(m_img.height()); + + setValid(true); + + return true; +} + +KisPaintDeviceSP KisPattern::image(KisColorSpace * colorSpace) { + // Check if there's already a pattern prepared for this colorspace + TQMap::const_iterator it = m_colorspaces.find(colorSpace->id().id()); + if (it != m_colorspaces.end()) + return (*it); + + // If not, create one + KisPaintDeviceSP layer = new KisPaintDevice(colorSpace, "pattern"); + + TQ_CHECK_PTR(layer); + + layer->convertFromTQImage(m_img,""); + + m_colorspaces[colorSpace->id().id()] = layer; + return layer; +} + +TQ_INT32 KisPattern::width() const +{ + return m_width; +} + +void KisPattern::setWidth(TQ_INT32 w) +{ + m_width = w; +} + +TQ_INT32 KisPattern::height() const +{ + return m_height; +} + +void KisPattern::setHeight(TQ_INT32 h) +{ + m_height = h; +} + +void KisPattern::setImage(const TQImage& img) +{ + m_hasFile = false; + m_img = img; + m_img.detach(); + + setWidth(img.width()); + setHeight(img.height()); + + setValid(true); +} + +KisPattern* KisPattern::clone() const +{ + KisPattern* pattern = new KisPattern(""); + pattern->setImage(m_img); + pattern->setName(name()); + return pattern; +} + +#include "kis_pattern.moc" diff --git a/chalk/core/kis_rect.cc b/chalk/core/kis_rect.cc deleted file mode 100644 index 175a07a9..00000000 --- a/chalk/core/kis_rect.cc +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2004 Adrian Page - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include - -#include "kis_rect.h" - -TQRect KisRect::qRect() const -{ - return TQRect(static_cast(floor(left())), static_cast(floor(top())), static_cast(ceil(right()) - floor(left())), static_cast(ceil(bottom()) - floor(top()))); -} - diff --git a/chalk/core/kis_rect.cpp b/chalk/core/kis_rect.cpp new file mode 100644 index 00000000..175a07a9 --- /dev/null +++ b/chalk/core/kis_rect.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2004 Adrian Page + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "kis_rect.h" + +TQRect KisRect::qRect() const +{ + return TQRect(static_cast(floor(left())), static_cast(floor(top())), static_cast(ceil(right()) - floor(left())), static_cast(ceil(bottom()) - floor(top()))); +} + diff --git a/chalk/core/kis_resource.cc b/chalk/core/kis_resource.cc deleted file mode 100644 index a856cbb1..00000000 --- a/chalk/core/kis_resource.cc +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2003 Patrick Julien - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_resource.h" -#include "kis_global.h" - -KisResource::KisResource(const TQString& filename) -{ - m_filename = filename; - m_valid = false; -} - -KisResource::~KisResource() -{ -} - -TQString KisResource::filename() const -{ - return m_filename; -} - -void KisResource::setFilename(const TQString& filename) -{ - m_filename = filename; -} - -TQString KisResource::name() const -{ - return m_name; -} - -void KisResource::setName(const TQString& name) -{ - m_name = name; -} - -bool KisResource::valid() const -{ - return m_valid; -} - -void KisResource::setValid(bool valid) -{ - m_valid = valid; -} - -#include "kis_resource.moc" - diff --git a/chalk/core/kis_resource.cpp b/chalk/core/kis_resource.cpp new file mode 100644 index 00000000..a856cbb1 --- /dev/null +++ b/chalk/core/kis_resource.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003 Patrick Julien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_resource.h" +#include "kis_global.h" + +KisResource::KisResource(const TQString& filename) +{ + m_filename = filename; + m_valid = false; +} + +KisResource::~KisResource() +{ +} + +TQString KisResource::filename() const +{ + return m_filename; +} + +void KisResource::setFilename(const TQString& filename) +{ + m_filename = filename; +} + +TQString KisResource::name() const +{ + return m_name; +} + +void KisResource::setName(const TQString& name) +{ + m_name = name; +} + +bool KisResource::valid() const +{ + return m_valid; +} + +void KisResource::setValid(bool valid) +{ + m_valid = valid; +} + +#include "kis_resource.moc" + diff --git a/chalk/core/kis_rotate_visitor.cc b/chalk/core/kis_rotate_visitor.cc deleted file mode 100644 index abb47a05..00000000 --- a/chalk/core/kis_rotate_visitor.cc +++ /dev/null @@ -1,406 +0,0 @@ -/* - * Copyright (c) 2004 Michael Thaler - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include -#include -#include - -#include -#include - -#include "kis_paint_device.h" -#include "kis_rotate_visitor.h" -#include "kis_progress_display_interface.h" -#include "kis_iterators_pixel.h" -#include "kis_selection.h" -#include "kis_painter.h" - -void KisRotateVisitor::rotate(double angle, bool rotateAboutImageCentre, KisProgressDisplayInterface *progress) -{ - KisPoint centreOfRotation; - - if (rotateAboutImageCentre) { - centreOfRotation = KisPoint(m_dev->image()->width() / 2.0, m_dev->image()->height() / 2.0); - } else { - TQRect r = m_dev->exactBounds(); - centreOfRotation = KisPoint(r.x() + (r.width() / 2.0), r.y() + (r.height() / 2.0)); - } - - m_progress = progress; - - KisPaintDeviceSP rotated = rotate(m_dev, angle, centreOfRotation); - - if (!m_dev->hasSelection()) { - // Clear everything - m_dev->clear(); - } else { - // Clear selected pixels - m_dev->clearSelection(); - } - - KisPainter p(m_dev); - TQRect r = rotated->extent(); - - // OVER ipv COPY - p.bitBlt(r.x(), r.y(), COMPOSITE_OVER, rotated, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height()); - p.end(); -} - -void KisRotateVisitor::shear(double angleX, double angleY, KisProgressDisplayInterface *progress) -{ - const double pi=3.1415926535897932385; - double thetaX = angleX * pi / 180; - double shearX = tan(thetaX); - double thetaY = angleY * pi / 180; - double shearY = tan(thetaY); - - TQRect r = m_dev->exactBounds(); - - const int xShearSteps = r.height(); - const int yShearSteps = r.width(); - - m_progress = progress; - initProgress(xShearSteps + yShearSteps); - - - KisPaintDeviceSP sheared; - - if (m_dev->hasSelection()) { - sheared = new KisPaintDevice(m_dev->colorSpace(), "sheared"); - KisPainter p1(sheared); - p1.bltSelection(r.x(), r.y(), COMPOSITE_OVER, m_dev, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height()); - p1.end(); - sheared = xShear(sheared, shearX); - } - else { - sheared = xShear(m_dev, shearX); - } - - sheared = yShear(sheared, shearY); - - if (!m_dev->hasSelection()) { - m_dev->clear(); - } else { - // Clear selected pixels - m_dev->clearSelection(); - } - - KisPainter p2(m_dev); - r = sheared->extent(); - - p2.bitBlt(r.x(), r.y(), COMPOSITE_OVER, sheared, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height()); - p2.end(); - - setProgressDone(); -} - -KisPaintDeviceSP KisRotateVisitor::rotateRight90(KisPaintDeviceSP src) -{ - KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotateright90"); - dst->setX(src->getX()); - dst->setY(src->getY()); - - TQ_INT32 pixelSize = src->pixelSize(); - TQRect r = src->exactBounds(); - TQ_INT32 x = 0; - - for (TQ_INT32 y = r.bottom(); y >= r.top(); --y) { - KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), false); - KisVLineIterator vit = dst->createVLineIterator(-y, r.x(), r.width(), true); - - while (!hit.isDone()) { - if (hit.isSelected()) { - memcpy(vit.rawData(), hit.rawData(), pixelSize); - } - ++hit; - ++vit; - } - ++x; - incrementProgress(); - } - - return dst; -} - -KisPaintDeviceSP KisRotateVisitor::rotateLeft90(KisPaintDeviceSP src) -{ - KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotateleft90"); - - TQ_INT32 pixelSize = src->pixelSize(); - TQRect r = src->exactBounds(); - TQ_INT32 x = 0; - - for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { - // Read the horizontal line from back to front, write onto the vertical column - KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), false); - KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true); - - hit += r.width() - 1; - while (!vit.isDone()) { - if (hit.isSelected()) { - memcpy(vit.rawData(), hit.rawData(), pixelSize); - } - --hit; - ++vit; - } - ++x; - incrementProgress(); - } - - return dst; -} - -KisPaintDeviceSP KisRotateVisitor::rotate180(KisPaintDeviceSP src) -{ - KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotate180"); - dst->setX(src->getX()); - dst->setY(src->getY()); - - TQ_INT32 pixelSize = src->pixelSize(); - TQRect r = src->exactBounds(); - - for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { - KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), false); - KisHLineIterator dstIt = dst->createHLineIterator( -r.x() - r.width(), -y, r.width(), true); - - srcIt += r.width() - 1; - while (!dstIt.isDone()) { - if (srcIt.isSelected()) { - memcpy(dstIt.rawData(), srcIt.rawData(), pixelSize); - } - --srcIt; - ++dstIt; - } - incrementProgress(); - } - - return dst; -} - -KisPaintDeviceSP KisRotateVisitor::rotate(KisPaintDeviceSP src, double angle, KisPoint centreOfRotation) -{ - const double pi = 3.1415926535897932385; - - if (angle >= 315 && angle < 360) { - angle = angle - 360; - } else if (angle > -360 && angle < -45) { - angle = angle + 360; - } - - TQRect r = src->exactBounds(); - - const int xShearSteps = r.height(); - const int yShearSteps = r.width(); - const int fixedRotateSteps = r.height(); - - KisPaintDeviceSP dst; - - if (angle == 90) { - initProgress(fixedRotateSteps); - dst = rotateRight90(src); - } else if (angle == 180) { - initProgress(fixedRotateSteps); - dst = rotate180(src); - } else if (angle == 270) { - initProgress(fixedRotateSteps); - dst = rotateLeft90(src); - } else { - double theta; - - if (angle >= -45 && angle < 45) { - - theta = angle * pi / 180; - dst = src; - initProgress(yShearSteps + (2 * xShearSteps)); - } - else if (angle >= 45 && angle < 135) { - - initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps)); - dst = rotateRight90(src); - theta = (angle - 90) * pi / 180; - } - else if (angle >= 135 && angle < 225) { - - initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps)); - dst = rotate180(src); - theta = (angle - 180) * pi / 180; - - } else { - - initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps)); - dst = rotateLeft90(src); - theta = (angle - 270) * pi / 180; - } - - double shearX = tan(theta / 2); - double shearY = sin(theta); - - //first perform a shear along the x-axis by tan(theta/2) - dst = xShear(dst, shearX); - //next perform a shear along the y-axis by sin(theta) - dst = yShear(dst, shearY); - //again perform a shear along the x-axis by tan(theta/2) - dst = xShear(dst, shearX); - } - - double sinAngle = sin(angle * pi / 180); - double cosAngle = cos(angle * pi / 180); - - KisPoint rotatedCentreOfRotation( - centreOfRotation.x() * cosAngle - centreOfRotation.y() * sinAngle, - centreOfRotation.x() * sinAngle + centreOfRotation.y() * cosAngle); - - dst->setX((TQ_INT32)(dst->getX() + centreOfRotation.x() - rotatedCentreOfRotation.x())); - dst->setY((TQ_INT32)(dst->getY() + centreOfRotation.y() - rotatedCentreOfRotation.y())); - - setProgressDone(); - - return dst; -} - -KisPaintDeviceSP KisRotateVisitor::xShear(KisPaintDeviceSP src, double shearX) -{ - KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "xShear"); - dst->setX(src->getX()); - dst->setY(src->getY()); - - TQRect r = src->exactBounds(); - - double displacement; - TQ_INT32 displacementInt; - double weight; - - for (TQ_INT32 y = r.top(); y <= r.bottom(); y++) { - - //calculate displacement - displacement = -y * shearX; - - displacementInt = (TQ_INT32)(floor(displacement)); - weight = displacement - displacementInt; - - TQ_UINT8 pixelWeights[2]; - - pixelWeights[0] = static_cast(weight * 255 + 0.5); - pixelWeights[1] = 255 - pixelWeights[0]; - - KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width() + 1, false); - KisHLineIteratorPixel leftSrcIt = src->createHLineIterator(r.x() - 1, y, r.width() + 1, false); - KisHLineIteratorPixel dstIt = dst->createHLineIterator(r.x() + displacementInt, y, r.width() + 1, true); - - while (!srcIt.isDone()) { - - const TQ_UINT8 *pixelPtrs[2]; - - pixelPtrs[0] = leftSrcIt.rawData(); - pixelPtrs[1] = srcIt.rawData(); - - src->colorSpace()->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData()); - - ++srcIt; - ++leftSrcIt; - ++dstIt; - } - incrementProgress(); - } - - return dst; -} - -KisPaintDeviceSP KisRotateVisitor::yShear(KisPaintDeviceSP src, double shearY) -{ - KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "yShear"); - dst->setX(src->getX()); - dst->setY(src->getY()); - - TQRect r = src->exactBounds(); - - double displacement; - TQ_INT32 displacementInt; - double weight; - - for (TQ_INT32 x = r.left(); x <= r.right(); x++) { - - //calculate displacement - displacement = x * shearY; - - displacementInt = (TQ_INT32)(floor(displacement)); - weight = displacement - displacementInt; - - TQ_UINT8 pixelWeights[2]; - - pixelWeights[0] = static_cast(weight * 255 + 0.5); - pixelWeights[1] = 255 - pixelWeights[0]; - - KisVLineIteratorPixel srcIt = src->createVLineIterator(x, r.y(), r.height() + 1, false); - KisVLineIteratorPixel leftSrcIt = src->createVLineIterator(x, r.y() - 1, r.height() + 1, false); - KisVLineIteratorPixel dstIt = dst->createVLineIterator(x, r.y() + displacementInt, r.height() + 1, true); - - while (!srcIt.isDone()) { - - const TQ_UINT8 *pixelPtrs[2]; - - pixelPtrs[0] = leftSrcIt.rawData(); - pixelPtrs[1] = srcIt.rawData(); - - src->colorSpace()->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData()); - - ++srcIt; - ++leftSrcIt; - ++dstIt; - } - incrementProgress(); - } - - return dst; -} - -void KisRotateVisitor::initProgress(TQ_INT32 totalSteps) -{ - if (!m_progress) return; - - m_progressTotalSteps = totalSteps; - m_progressStep = 0; - m_lastProgressPerCent = 0; - - - m_progress->setSubject(this, true, false); - emit notifyProgress(0); - -} - -void KisRotateVisitor::incrementProgress() -{ - if (!m_progress) return; - - m_progressStep++; - TQ_INT32 progressPerCent = (m_progressStep * 100) / m_progressTotalSteps; - - if (progressPerCent != m_lastProgressPerCent) { - m_lastProgressPerCent = progressPerCent; - emit notifyProgress(progressPerCent); - } -} - -void KisRotateVisitor::setProgressDone() -{ - if (!m_progress) return; - - emit notifyProgressDone(); -} - - diff --git a/chalk/core/kis_rotate_visitor.cpp b/chalk/core/kis_rotate_visitor.cpp new file mode 100644 index 00000000..abb47a05 --- /dev/null +++ b/chalk/core/kis_rotate_visitor.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2004 Michael Thaler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include + +#include +#include + +#include "kis_paint_device.h" +#include "kis_rotate_visitor.h" +#include "kis_progress_display_interface.h" +#include "kis_iterators_pixel.h" +#include "kis_selection.h" +#include "kis_painter.h" + +void KisRotateVisitor::rotate(double angle, bool rotateAboutImageCentre, KisProgressDisplayInterface *progress) +{ + KisPoint centreOfRotation; + + if (rotateAboutImageCentre) { + centreOfRotation = KisPoint(m_dev->image()->width() / 2.0, m_dev->image()->height() / 2.0); + } else { + TQRect r = m_dev->exactBounds(); + centreOfRotation = KisPoint(r.x() + (r.width() / 2.0), r.y() + (r.height() / 2.0)); + } + + m_progress = progress; + + KisPaintDeviceSP rotated = rotate(m_dev, angle, centreOfRotation); + + if (!m_dev->hasSelection()) { + // Clear everything + m_dev->clear(); + } else { + // Clear selected pixels + m_dev->clearSelection(); + } + + KisPainter p(m_dev); + TQRect r = rotated->extent(); + + // OVER ipv COPY + p.bitBlt(r.x(), r.y(), COMPOSITE_OVER, rotated, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height()); + p.end(); +} + +void KisRotateVisitor::shear(double angleX, double angleY, KisProgressDisplayInterface *progress) +{ + const double pi=3.1415926535897932385; + double thetaX = angleX * pi / 180; + double shearX = tan(thetaX); + double thetaY = angleY * pi / 180; + double shearY = tan(thetaY); + + TQRect r = m_dev->exactBounds(); + + const int xShearSteps = r.height(); + const int yShearSteps = r.width(); + + m_progress = progress; + initProgress(xShearSteps + yShearSteps); + + + KisPaintDeviceSP sheared; + + if (m_dev->hasSelection()) { + sheared = new KisPaintDevice(m_dev->colorSpace(), "sheared"); + KisPainter p1(sheared); + p1.bltSelection(r.x(), r.y(), COMPOSITE_OVER, m_dev, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height()); + p1.end(); + sheared = xShear(sheared, shearX); + } + else { + sheared = xShear(m_dev, shearX); + } + + sheared = yShear(sheared, shearY); + + if (!m_dev->hasSelection()) { + m_dev->clear(); + } else { + // Clear selected pixels + m_dev->clearSelection(); + } + + KisPainter p2(m_dev); + r = sheared->extent(); + + p2.bitBlt(r.x(), r.y(), COMPOSITE_OVER, sheared, OPACITY_OPAQUE, r.x(), r.y(), r.width(), r.height()); + p2.end(); + + setProgressDone(); +} + +KisPaintDeviceSP KisRotateVisitor::rotateRight90(KisPaintDeviceSP src) +{ + KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotateright90"); + dst->setX(src->getX()); + dst->setY(src->getY()); + + TQ_INT32 pixelSize = src->pixelSize(); + TQRect r = src->exactBounds(); + TQ_INT32 x = 0; + + for (TQ_INT32 y = r.bottom(); y >= r.top(); --y) { + KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), false); + KisVLineIterator vit = dst->createVLineIterator(-y, r.x(), r.width(), true); + + while (!hit.isDone()) { + if (hit.isSelected()) { + memcpy(vit.rawData(), hit.rawData(), pixelSize); + } + ++hit; + ++vit; + } + ++x; + incrementProgress(); + } + + return dst; +} + +KisPaintDeviceSP KisRotateVisitor::rotateLeft90(KisPaintDeviceSP src) +{ + KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotateleft90"); + + TQ_INT32 pixelSize = src->pixelSize(); + TQRect r = src->exactBounds(); + TQ_INT32 x = 0; + + for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { + // Read the horizontal line from back to front, write onto the vertical column + KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), false); + KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true); + + hit += r.width() - 1; + while (!vit.isDone()) { + if (hit.isSelected()) { + memcpy(vit.rawData(), hit.rawData(), pixelSize); + } + --hit; + ++vit; + } + ++x; + incrementProgress(); + } + + return dst; +} + +KisPaintDeviceSP KisRotateVisitor::rotate180(KisPaintDeviceSP src) +{ + KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "rotate180"); + dst->setX(src->getX()); + dst->setY(src->getY()); + + TQ_INT32 pixelSize = src->pixelSize(); + TQRect r = src->exactBounds(); + + for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { + KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), false); + KisHLineIterator dstIt = dst->createHLineIterator( -r.x() - r.width(), -y, r.width(), true); + + srcIt += r.width() - 1; + while (!dstIt.isDone()) { + if (srcIt.isSelected()) { + memcpy(dstIt.rawData(), srcIt.rawData(), pixelSize); + } + --srcIt; + ++dstIt; + } + incrementProgress(); + } + + return dst; +} + +KisPaintDeviceSP KisRotateVisitor::rotate(KisPaintDeviceSP src, double angle, KisPoint centreOfRotation) +{ + const double pi = 3.1415926535897932385; + + if (angle >= 315 && angle < 360) { + angle = angle - 360; + } else if (angle > -360 && angle < -45) { + angle = angle + 360; + } + + TQRect r = src->exactBounds(); + + const int xShearSteps = r.height(); + const int yShearSteps = r.width(); + const int fixedRotateSteps = r.height(); + + KisPaintDeviceSP dst; + + if (angle == 90) { + initProgress(fixedRotateSteps); + dst = rotateRight90(src); + } else if (angle == 180) { + initProgress(fixedRotateSteps); + dst = rotate180(src); + } else if (angle == 270) { + initProgress(fixedRotateSteps); + dst = rotateLeft90(src); + } else { + double theta; + + if (angle >= -45 && angle < 45) { + + theta = angle * pi / 180; + dst = src; + initProgress(yShearSteps + (2 * xShearSteps)); + } + else if (angle >= 45 && angle < 135) { + + initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps)); + dst = rotateRight90(src); + theta = (angle - 90) * pi / 180; + } + else if (angle >= 135 && angle < 225) { + + initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps)); + dst = rotate180(src); + theta = (angle - 180) * pi / 180; + + } else { + + initProgress(fixedRotateSteps + yShearSteps + (2 * xShearSteps)); + dst = rotateLeft90(src); + theta = (angle - 270) * pi / 180; + } + + double shearX = tan(theta / 2); + double shearY = sin(theta); + + //first perform a shear along the x-axis by tan(theta/2) + dst = xShear(dst, shearX); + //next perform a shear along the y-axis by sin(theta) + dst = yShear(dst, shearY); + //again perform a shear along the x-axis by tan(theta/2) + dst = xShear(dst, shearX); + } + + double sinAngle = sin(angle * pi / 180); + double cosAngle = cos(angle * pi / 180); + + KisPoint rotatedCentreOfRotation( + centreOfRotation.x() * cosAngle - centreOfRotation.y() * sinAngle, + centreOfRotation.x() * sinAngle + centreOfRotation.y() * cosAngle); + + dst->setX((TQ_INT32)(dst->getX() + centreOfRotation.x() - rotatedCentreOfRotation.x())); + dst->setY((TQ_INT32)(dst->getY() + centreOfRotation.y() - rotatedCentreOfRotation.y())); + + setProgressDone(); + + return dst; +} + +KisPaintDeviceSP KisRotateVisitor::xShear(KisPaintDeviceSP src, double shearX) +{ + KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "xShear"); + dst->setX(src->getX()); + dst->setY(src->getY()); + + TQRect r = src->exactBounds(); + + double displacement; + TQ_INT32 displacementInt; + double weight; + + for (TQ_INT32 y = r.top(); y <= r.bottom(); y++) { + + //calculate displacement + displacement = -y * shearX; + + displacementInt = (TQ_INT32)(floor(displacement)); + weight = displacement - displacementInt; + + TQ_UINT8 pixelWeights[2]; + + pixelWeights[0] = static_cast(weight * 255 + 0.5); + pixelWeights[1] = 255 - pixelWeights[0]; + + KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width() + 1, false); + KisHLineIteratorPixel leftSrcIt = src->createHLineIterator(r.x() - 1, y, r.width() + 1, false); + KisHLineIteratorPixel dstIt = dst->createHLineIterator(r.x() + displacementInt, y, r.width() + 1, true); + + while (!srcIt.isDone()) { + + const TQ_UINT8 *pixelPtrs[2]; + + pixelPtrs[0] = leftSrcIt.rawData(); + pixelPtrs[1] = srcIt.rawData(); + + src->colorSpace()->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData()); + + ++srcIt; + ++leftSrcIt; + ++dstIt; + } + incrementProgress(); + } + + return dst; +} + +KisPaintDeviceSP KisRotateVisitor::yShear(KisPaintDeviceSP src, double shearY) +{ + KisPaintDeviceSP dst = new KisPaintDevice(src->colorSpace(), "yShear"); + dst->setX(src->getX()); + dst->setY(src->getY()); + + TQRect r = src->exactBounds(); + + double displacement; + TQ_INT32 displacementInt; + double weight; + + for (TQ_INT32 x = r.left(); x <= r.right(); x++) { + + //calculate displacement + displacement = x * shearY; + + displacementInt = (TQ_INT32)(floor(displacement)); + weight = displacement - displacementInt; + + TQ_UINT8 pixelWeights[2]; + + pixelWeights[0] = static_cast(weight * 255 + 0.5); + pixelWeights[1] = 255 - pixelWeights[0]; + + KisVLineIteratorPixel srcIt = src->createVLineIterator(x, r.y(), r.height() + 1, false); + KisVLineIteratorPixel leftSrcIt = src->createVLineIterator(x, r.y() - 1, r.height() + 1, false); + KisVLineIteratorPixel dstIt = dst->createVLineIterator(x, r.y() + displacementInt, r.height() + 1, true); + + while (!srcIt.isDone()) { + + const TQ_UINT8 *pixelPtrs[2]; + + pixelPtrs[0] = leftSrcIt.rawData(); + pixelPtrs[1] = srcIt.rawData(); + + src->colorSpace()->mixColors(pixelPtrs, pixelWeights, 2, dstIt.rawData()); + + ++srcIt; + ++leftSrcIt; + ++dstIt; + } + incrementProgress(); + } + + return dst; +} + +void KisRotateVisitor::initProgress(TQ_INT32 totalSteps) +{ + if (!m_progress) return; + + m_progressTotalSteps = totalSteps; + m_progressStep = 0; + m_lastProgressPerCent = 0; + + + m_progress->setSubject(this, true, false); + emit notifyProgress(0); + +} + +void KisRotateVisitor::incrementProgress() +{ + if (!m_progress) return; + + m_progressStep++; + TQ_INT32 progressPerCent = (m_progressStep * 100) / m_progressTotalSteps; + + if (progressPerCent != m_lastProgressPerCent) { + m_lastProgressPerCent = progressPerCent; + emit notifyProgress(progressPerCent); + } +} + +void KisRotateVisitor::setProgressDone() +{ + if (!m_progress) return; + + emit notifyProgressDone(); +} + + diff --git a/chalk/core/kis_scale_visitor.cc b/chalk/core/kis_scale_visitor.cc deleted file mode 100644 index a06d674d..00000000 --- a/chalk/core/kis_scale_visitor.cc +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (c) 2004, 2005 Michael Thaler - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include - -#include -#include - -#include "kis_paint_device.h" -#include "kis_scale_visitor.h" -#include "kis_filter_strategy.h" - - -void KisScaleWorker::run() -{ - double fwidth = m_filterStrategy->support(); - - TQRect rect = m_dev -> exactBounds(); - TQ_INT32 width = rect.width(); - TQ_INT32 height = rect.height(); - m_pixelSize=m_dev -> pixelSize(); - - // compute size of target image - if ( m_sx == 1.0F && m_sy == 1.0F ) { - return; - } - TQ_INT32 targetW = TQABS( tqRound( m_sx * width ) ); - TQ_INT32 targetH = TQABS( tqRound( m_sy * height ) ); - - TQ_UINT8* newData = new TQ_UINT8[targetW * targetH * m_pixelSize ]; - TQ_CHECK_PTR(newData); - - double* weight = new double[ m_pixelSize ]; /* filter calculation variables */ - - TQ_UINT8* pel = new TQ_UINT8[ m_pixelSize ]; - TQ_CHECK_PTR(pel); - - TQ_UINT8 *pel2 = new TQ_UINT8[ m_pixelSize ]; - TQ_CHECK_PTR(pel2); - - bool* bPelDelta = new bool[ m_pixelSize ]; - ContribList *contribX; - ContribList contribY; - const TQ_INT32 BLACK_PIXEL=0; - const TQ_INT32 WHITE_PIXEL=255; - - - // create intermediate row to hold vertical dst row zoom - TQ_UINT8 * tmp = new TQ_UINT8[ width * m_pixelSize ]; - TQ_CHECK_PTR(tmp); - - //create array of pointers to intermediate rows - TQ_UINT8 **tmpRows = new TQ_UINT8*[ height ]; - - //create array of pointers to intermediate rows that are actually used simultaneously and allocate memory for the rows - TQ_UINT8 **tmpRowsMem; - if(m_sy < 1.0) - { - tmpRowsMem = new TQ_UINT8*[ (int)(fwidth / m_sy * 2 + 1) ]; - for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++) - { - tmpRowsMem[i] = new TQ_UINT8[ width * m_pixelSize ]; - TQ_CHECK_PTR(tmpRowsMem[i]); - } - } - else - { - tmpRowsMem = new TQ_UINT8*[ (int)(fwidth * 2 + 1) ]; - for(int i = 0; i < (int)(fwidth * 2 + 1); i++) - { - tmpRowsMem[i] = new TQ_UINT8[ width * m_pixelSize ]; - TQ_CHECK_PTR(tmpRowsMem[i]); - } - } - - // build x weights - contribX = new ContribList[ targetW ]; - for(int x = 0; x < targetW; x++) - { - calcContrib(&contribX[x], m_sx, fwidth, width, m_filterStrategy, x); - } - - TQTime starttime = TQTime::currentTime (); - - for(int y = 0; y < targetH; y++) - { - // build y weights - calcContrib(&contribY, m_sy, fwidth, height, m_filterStrategy, y); - - //copy pixel data to temporary arrays - for(int srcpos = 0; srcpos < contribY.n; srcpos++) - { - if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height)) - { - tmpRows[contribY.p[srcpos].m_pixel] = new TQ_UINT8[ width * m_pixelSize ]; - //tmpRows[ contribY.p[srcpos].m_pixel ] = tmpRowsMem[ srcpos ]; - m_dev ->readBytes(tmpRows[contribY.p[srcpos].m_pixel], 0, contribY.p[srcpos].m_pixel, width, 1); - } - } - - /* Apply vert filter to make dst row in tmp. */ - for(int x = 0; x < width; x++) - { - for(int channel = 0; channel < m_pixelSize; channel++){ - weight[channel] = 0.0; - bPelDelta[channel] = FALSE; - pel[channel]=tmpRows[contribY.p[0].m_pixel][ x * m_pixelSize + channel ]; - } - for(int srcpos = 0; srcpos < contribY.n; srcpos++) - { - if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height)){ - for(int channel = 0; channel < m_pixelSize; channel++) - { - pel2[channel]=tmpRows[contribY.p[srcpos].m_pixel][ x * m_pixelSize + channel ]; - if(pel2[channel] != pel[channel]) bPelDelta[channel] = TRUE; - weight[channel] += pel2[channel] * contribY.p[srcpos].m_weight; - } - } - } - - for(int channel = 0; channel < m_pixelSize; channel++){ - weight[channel] = bPelDelta[channel] ? static_cast(tqRound(weight[channel])) : pel[channel]; - tmp[ x * m_pixelSize + channel ] = static_cast(CLAMP(weight[channel], BLACK_PIXEL, WHITE_PIXEL)); - } - } /* next row in temp column */ - delete[] contribY.p; - - for(int x = 0; x < targetW; x++) - { - for(int channel = 0; channel < m_pixelSize; channel++){ - weight[channel] = 0.0; - bPelDelta[channel] = FALSE; - pel[channel] = tmp[ contribX[x].p[0].m_pixel * m_pixelSize + channel ]; - } - for(int srcpos = 0; srcpos < contribX[x].n; srcpos++) - { - for(int channel = 0; channel < m_pixelSize; channel++){ - pel2[channel] = tmp[ contribX[x].p[srcpos].m_pixel * m_pixelSize + channel ]; - if(pel2[channel] != pel[channel]) - bPelDelta[channel] = TRUE; - weight[channel] += pel2[channel] * contribX[x].p[srcpos].m_weight; - } - } - for(int channel = 0; channel < m_pixelSize; channel++){ - weight[channel] = bPelDelta[channel] ? static_cast(tqRound(weight[channel])) : pel[channel]; - int currentPos = (y*targetW+x) * m_pixelSize; // try to be at least a little efficient - if (weight[channel]<0) - newData[currentPos + channel] = 0; - else if (weight[channel]>255) - newData[currentPos + channel] = 255; - else - newData[currentPos + channel] = (uchar)weight[channel]; - } - } /* next dst row */ - } /* next dst column */ - - // XXX: I'm thinking that we should be able to cancel earlier, in the look. - if(!isCanceled()){ - m_dev -> writeBytes( newData, 0, 0, targetW, targetH); - m_dev -> crop(0, 0, targetW, targetH); - } - - /* free the memory allocated for horizontal filter weights */ - for(int x = 0; x < targetW; x++) - delete[] contribX[x].p; - delete[] contribX; - - delete[] newData; - delete[] pel; - delete[] pel2; - delete[] tmp; - delete[] weight; - delete[] bPelDelta; - - if(m_sy < 1.0) - { - for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++) - { - delete[] tmpRowsMem[i]; - } - } - else - { - for(int i = 0; i < (int)(fwidth * 2 + 1); i++) - { - delete[] tmpRowsMem[i]; - } - } - - TQTime stoptime = TQTime::currentTime (); - return; -} - -int KisScaleWorker::calcContrib(ContribList *contrib, double scale, double fwidth, int srcwidth, KisFilterStrategy* filterStrategy, TQ_INT32 i) -{ - //ContribList* contribX: receiver of contrib info - //double m_sx: horizontal zooming scale - //double fwidth: Filter sampling width - //int dstwidth: Target bitmap width - //int srcwidth: Source bitmap width - //double (*filterf)(double): Filter proc - //int i: Pixel column in source bitmap being processed - - double width; - double fscale; - double center, begin, end; - double weight; - TQ_INT32 k, n; - - if(scale < 1.0) - { - //Shrinking image - width = fwidth / scale; - fscale = 1.0 / scale; - - contrib->n = 0; - contrib->p = new Contrib[ (int)(width * 2 + 1) ]; - - center = (double) i / scale; - begin = ceil(center - width); - end = floor(center + width); - for(int srcpos = (int)begin; srcpos <= end; ++srcpos) - { - weight = center - (double) srcpos; - weight = filterStrategy->valueAt(weight / fscale) / fscale; - if(srcpos < 0) - n = -srcpos; - else if(srcpos >= srcwidth) - n = (srcwidth - srcpos) + srcwidth - 1; - else - n = srcpos; - - k = contrib->n++; - contrib->p[k].m_pixel = n; - contrib->p[k].m_weight = weight; - } - } - else - { - // Expanding image - contrib->n = 0; - contrib->p = new Contrib[ (int)(fwidth * 2 + 1) ]; - - center = (double) i / scale; - begin = ceil(center - fwidth); - end = floor(center + fwidth); - - for(int srcpos = (int)begin; srcpos <= end; ++srcpos) - { - weight = center - (double) srcpos; - weight = filterStrategy->valueAt(weight); - if(srcpos < 0) { - n = -srcpos; - } else if(srcpos >= srcwidth) { - n = (srcwidth - srcpos) + srcwidth - 1; - } else { - n = srcpos; - } - k = contrib->n++; - contrib->p[k].m_pixel = n; - contrib->p[k].m_weight = weight; - } - } - return 0; -} /* calc_x_contrib */ diff --git a/chalk/core/kis_scale_visitor.cpp b/chalk/core/kis_scale_visitor.cpp new file mode 100644 index 00000000..a06d674d --- /dev/null +++ b/chalk/core/kis_scale_visitor.cpp @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2004, 2005 Michael Thaler + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include + +#include +#include + +#include "kis_paint_device.h" +#include "kis_scale_visitor.h" +#include "kis_filter_strategy.h" + + +void KisScaleWorker::run() +{ + double fwidth = m_filterStrategy->support(); + + TQRect rect = m_dev -> exactBounds(); + TQ_INT32 width = rect.width(); + TQ_INT32 height = rect.height(); + m_pixelSize=m_dev -> pixelSize(); + + // compute size of target image + if ( m_sx == 1.0F && m_sy == 1.0F ) { + return; + } + TQ_INT32 targetW = TQABS( tqRound( m_sx * width ) ); + TQ_INT32 targetH = TQABS( tqRound( m_sy * height ) ); + + TQ_UINT8* newData = new TQ_UINT8[targetW * targetH * m_pixelSize ]; + TQ_CHECK_PTR(newData); + + double* weight = new double[ m_pixelSize ]; /* filter calculation variables */ + + TQ_UINT8* pel = new TQ_UINT8[ m_pixelSize ]; + TQ_CHECK_PTR(pel); + + TQ_UINT8 *pel2 = new TQ_UINT8[ m_pixelSize ]; + TQ_CHECK_PTR(pel2); + + bool* bPelDelta = new bool[ m_pixelSize ]; + ContribList *contribX; + ContribList contribY; + const TQ_INT32 BLACK_PIXEL=0; + const TQ_INT32 WHITE_PIXEL=255; + + + // create intermediate row to hold vertical dst row zoom + TQ_UINT8 * tmp = new TQ_UINT8[ width * m_pixelSize ]; + TQ_CHECK_PTR(tmp); + + //create array of pointers to intermediate rows + TQ_UINT8 **tmpRows = new TQ_UINT8*[ height ]; + + //create array of pointers to intermediate rows that are actually used simultaneously and allocate memory for the rows + TQ_UINT8 **tmpRowsMem; + if(m_sy < 1.0) + { + tmpRowsMem = new TQ_UINT8*[ (int)(fwidth / m_sy * 2 + 1) ]; + for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++) + { + tmpRowsMem[i] = new TQ_UINT8[ width * m_pixelSize ]; + TQ_CHECK_PTR(tmpRowsMem[i]); + } + } + else + { + tmpRowsMem = new TQ_UINT8*[ (int)(fwidth * 2 + 1) ]; + for(int i = 0; i < (int)(fwidth * 2 + 1); i++) + { + tmpRowsMem[i] = new TQ_UINT8[ width * m_pixelSize ]; + TQ_CHECK_PTR(tmpRowsMem[i]); + } + } + + // build x weights + contribX = new ContribList[ targetW ]; + for(int x = 0; x < targetW; x++) + { + calcContrib(&contribX[x], m_sx, fwidth, width, m_filterStrategy, x); + } + + TQTime starttime = TQTime::currentTime (); + + for(int y = 0; y < targetH; y++) + { + // build y weights + calcContrib(&contribY, m_sy, fwidth, height, m_filterStrategy, y); + + //copy pixel data to temporary arrays + for(int srcpos = 0; srcpos < contribY.n; srcpos++) + { + if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height)) + { + tmpRows[contribY.p[srcpos].m_pixel] = new TQ_UINT8[ width * m_pixelSize ]; + //tmpRows[ contribY.p[srcpos].m_pixel ] = tmpRowsMem[ srcpos ]; + m_dev ->readBytes(tmpRows[contribY.p[srcpos].m_pixel], 0, contribY.p[srcpos].m_pixel, width, 1); + } + } + + /* Apply vert filter to make dst row in tmp. */ + for(int x = 0; x < width; x++) + { + for(int channel = 0; channel < m_pixelSize; channel++){ + weight[channel] = 0.0; + bPelDelta[channel] = FALSE; + pel[channel]=tmpRows[contribY.p[0].m_pixel][ x * m_pixelSize + channel ]; + } + for(int srcpos = 0; srcpos < contribY.n; srcpos++) + { + if (!(contribY.p[srcpos].m_pixel < 0 || contribY.p[srcpos].m_pixel >= height)){ + for(int channel = 0; channel < m_pixelSize; channel++) + { + pel2[channel]=tmpRows[contribY.p[srcpos].m_pixel][ x * m_pixelSize + channel ]; + if(pel2[channel] != pel[channel]) bPelDelta[channel] = TRUE; + weight[channel] += pel2[channel] * contribY.p[srcpos].m_weight; + } + } + } + + for(int channel = 0; channel < m_pixelSize; channel++){ + weight[channel] = bPelDelta[channel] ? static_cast(tqRound(weight[channel])) : pel[channel]; + tmp[ x * m_pixelSize + channel ] = static_cast(CLAMP(weight[channel], BLACK_PIXEL, WHITE_PIXEL)); + } + } /* next row in temp column */ + delete[] contribY.p; + + for(int x = 0; x < targetW; x++) + { + for(int channel = 0; channel < m_pixelSize; channel++){ + weight[channel] = 0.0; + bPelDelta[channel] = FALSE; + pel[channel] = tmp[ contribX[x].p[0].m_pixel * m_pixelSize + channel ]; + } + for(int srcpos = 0; srcpos < contribX[x].n; srcpos++) + { + for(int channel = 0; channel < m_pixelSize; channel++){ + pel2[channel] = tmp[ contribX[x].p[srcpos].m_pixel * m_pixelSize + channel ]; + if(pel2[channel] != pel[channel]) + bPelDelta[channel] = TRUE; + weight[channel] += pel2[channel] * contribX[x].p[srcpos].m_weight; + } + } + for(int channel = 0; channel < m_pixelSize; channel++){ + weight[channel] = bPelDelta[channel] ? static_cast(tqRound(weight[channel])) : pel[channel]; + int currentPos = (y*targetW+x) * m_pixelSize; // try to be at least a little efficient + if (weight[channel]<0) + newData[currentPos + channel] = 0; + else if (weight[channel]>255) + newData[currentPos + channel] = 255; + else + newData[currentPos + channel] = (uchar)weight[channel]; + } + } /* next dst row */ + } /* next dst column */ + + // XXX: I'm thinking that we should be able to cancel earlier, in the look. + if(!isCanceled()){ + m_dev -> writeBytes( newData, 0, 0, targetW, targetH); + m_dev -> crop(0, 0, targetW, targetH); + } + + /* free the memory allocated for horizontal filter weights */ + for(int x = 0; x < targetW; x++) + delete[] contribX[x].p; + delete[] contribX; + + delete[] newData; + delete[] pel; + delete[] pel2; + delete[] tmp; + delete[] weight; + delete[] bPelDelta; + + if(m_sy < 1.0) + { + for(int i = 0; i < (int)(fwidth / m_sy * 2 + 1); i++) + { + delete[] tmpRowsMem[i]; + } + } + else + { + for(int i = 0; i < (int)(fwidth * 2 + 1); i++) + { + delete[] tmpRowsMem[i]; + } + } + + TQTime stoptime = TQTime::currentTime (); + return; +} + +int KisScaleWorker::calcContrib(ContribList *contrib, double scale, double fwidth, int srcwidth, KisFilterStrategy* filterStrategy, TQ_INT32 i) +{ + //ContribList* contribX: receiver of contrib info + //double m_sx: horizontal zooming scale + //double fwidth: Filter sampling width + //int dstwidth: Target bitmap width + //int srcwidth: Source bitmap width + //double (*filterf)(double): Filter proc + //int i: Pixel column in source bitmap being processed + + double width; + double fscale; + double center, begin, end; + double weight; + TQ_INT32 k, n; + + if(scale < 1.0) + { + //Shrinking image + width = fwidth / scale; + fscale = 1.0 / scale; + + contrib->n = 0; + contrib->p = new Contrib[ (int)(width * 2 + 1) ]; + + center = (double) i / scale; + begin = ceil(center - width); + end = floor(center + width); + for(int srcpos = (int)begin; srcpos <= end; ++srcpos) + { + weight = center - (double) srcpos; + weight = filterStrategy->valueAt(weight / fscale) / fscale; + if(srcpos < 0) + n = -srcpos; + else if(srcpos >= srcwidth) + n = (srcwidth - srcpos) + srcwidth - 1; + else + n = srcpos; + + k = contrib->n++; + contrib->p[k].m_pixel = n; + contrib->p[k].m_weight = weight; + } + } + else + { + // Expanding image + contrib->n = 0; + contrib->p = new Contrib[ (int)(fwidth * 2 + 1) ]; + + center = (double) i / scale; + begin = ceil(center - fwidth); + end = floor(center + fwidth); + + for(int srcpos = (int)begin; srcpos <= end; ++srcpos) + { + weight = center - (double) srcpos; + weight = filterStrategy->valueAt(weight); + if(srcpos < 0) { + n = -srcpos; + } else if(srcpos >= srcwidth) { + n = (srcwidth - srcpos) + srcwidth - 1; + } else { + n = srcpos; + } + k = contrib->n++; + contrib->p[k].m_pixel = n; + contrib->p[k].m_weight = weight; + } + } + return 0; +} /* calc_x_contrib */ diff --git a/chalk/core/kis_selected_transaction.cc b/chalk/core/kis_selected_transaction.cc deleted file mode 100644 index 44eec697..00000000 --- a/chalk/core/kis_selected_transaction.cc +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_types.h" -#include "kis_global.h" -#include "kis_selected_transaction.h" -#include "kis_selection.h" - -KisSelectedTransaction::KisSelectedTransaction(const TQString& name, KisPaintDeviceSP device) : - KisTransaction(name, device), - m_device(device), - m_hadSelection(device->hasSelection()) -{ - m_selTransaction = new KisTransaction(name, device->selection().data()); - if(! m_hadSelection) { - m_device->deselect(); // let us not be the cause of select - } -} - -KisSelectedTransaction::~KisSelectedTransaction() -{ - delete m_selTransaction; -} - -void KisSelectedTransaction::execute() -{ - super::execute(); - m_selTransaction->execute(); - if(m_redoHasSelection) - m_device->selection(); - else - m_device->deselect(); - m_device->emitSelectionChanged(); -} - -void KisSelectedTransaction::unexecute() -{ - m_redoHasSelection = m_device->hasSelection(); - - super::unexecute(); - m_selTransaction->unexecute(); - if(m_hadSelection) - m_device->selection(); - else - m_device->deselect(); - m_device->emitSelectionChanged(); -} - -void KisSelectedTransaction::unexecuteNoUpdate() -{ - m_redoHasSelection = m_device->hasSelection(); - - super::unexecuteNoUpdate(); - m_selTransaction->unexecuteNoUpdate(); - if(m_hadSelection) - m_device->selection(); - else - m_device->deselect(); -} diff --git a/chalk/core/kis_selected_transaction.cpp b/chalk/core/kis_selected_transaction.cpp new file mode 100644 index 00000000..44eec697 --- /dev/null +++ b/chalk/core/kis_selected_transaction.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002 Patrick Julien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_types.h" +#include "kis_global.h" +#include "kis_selected_transaction.h" +#include "kis_selection.h" + +KisSelectedTransaction::KisSelectedTransaction(const TQString& name, KisPaintDeviceSP device) : + KisTransaction(name, device), + m_device(device), + m_hadSelection(device->hasSelection()) +{ + m_selTransaction = new KisTransaction(name, device->selection().data()); + if(! m_hadSelection) { + m_device->deselect(); // let us not be the cause of select + } +} + +KisSelectedTransaction::~KisSelectedTransaction() +{ + delete m_selTransaction; +} + +void KisSelectedTransaction::execute() +{ + super::execute(); + m_selTransaction->execute(); + if(m_redoHasSelection) + m_device->selection(); + else + m_device->deselect(); + m_device->emitSelectionChanged(); +} + +void KisSelectedTransaction::unexecute() +{ + m_redoHasSelection = m_device->hasSelection(); + + super::unexecute(); + m_selTransaction->unexecute(); + if(m_hadSelection) + m_device->selection(); + else + m_device->deselect(); + m_device->emitSelectionChanged(); +} + +void KisSelectedTransaction::unexecuteNoUpdate() +{ + m_redoHasSelection = m_device->hasSelection(); + + super::unexecuteNoUpdate(); + m_selTransaction->unexecuteNoUpdate(); + if(m_hadSelection) + m_device->selection(); + else + m_device->deselect(); +} diff --git a/chalk/core/kis_selection.cc b/chalk/core/kis_selection.cc deleted file mode 100644 index ea8f2be2..00000000 --- a/chalk/core/kis_selection.cc +++ /dev/null @@ -1,582 +0,0 @@ -/* - * Copyright (c) 2004 Boudewijn Rempt - * - * this program is free software; you can redistribute it and/or modify - * it under the terms of the gnu general public license as published by - * the free software foundation; either version 2 of the license, or - * (at your option) any later version. - * - * this program is distributed in the hope that it will be useful, - * but without any warranty; without even the implied warranty of - * merchantability or fitness for a particular purpose. see the - * gnu general public license for more details. - * - * you should have received a copy of the gnu general public license - * along with this program; if not, write to the free software - * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. - */ - -#include - -#include -#include -#include - -#include "kis_layer.h" -#include "kis_debug_areas.h" -#include "kis_types.h" -#include "kis_colorspace_factory_registry.h" -#include "kis_fill_painter.h" -#include "kis_iterators_pixel.h" -#include "kis_integer_maths.h" -#include "kis_image.h" -#include "kis_datamanager.h" -#include "kis_fill_painter.h" -#include "kis_selection.h" - -KisSelection::KisSelection(KisPaintDeviceSP dev) - : super(dev->parentLayer() - , KisMetaRegistry::instance()->csRegistry()->getAlpha8() - , (TQString("selection for ") + dev->name()).latin1()) - , m_parentPaintDevice(dev) - , m_doCacheExactRect(false) - , m_dirty(false) -{ - Q_ASSERT(dev); -} - -KisSelection::KisSelection() - : super(KisMetaRegistry::instance()->csRegistry()->getAlpha8(), "anonymous selection") - , m_parentPaintDevice(0), m_dirty(false) -{ -} - -KisSelection::KisSelection(const KisSelection& rhs) - : super(rhs), m_parentPaintDevice(rhs.m_parentPaintDevice), m_doCacheExactRect(rhs.m_doCacheExactRect), - m_cachedExactRect(rhs.m_cachedExactRect), m_dirty(rhs.m_dirty) -{ -} - -KisSelection::~KisSelection() -{ -} - -TQ_UINT8 KisSelection::selected(TQ_INT32 x, TQ_INT32 y) -{ - KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false); - - TQ_UINT8 *pix = iter.rawData(); - - return *pix; -} - -void KisSelection::setSelected(TQ_INT32 x, TQ_INT32 y, TQ_UINT8 s) -{ - KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); - - TQ_UINT8 *pix = iter.rawData(); - - *pix = s; -} - -TQImage KisSelection::maskImage() -{ - // If part of a KisAdjustmentLayer, there may be no parent device. - TQImage img; - TQRect bounds; - if (m_parentPaintDevice) { - - bounds = m_parentPaintDevice->exactBounds(); - bounds = bounds.intersect( m_parentPaintDevice->image()->bounds() ); - img = TQImage(bounds.width(), bounds.height(), 32); - } - else { - bounds = TQRect( 0, 0, image()->width(), image()->height()); - img = TQImage(bounds.width(), bounds.height(), 32); - } - - KisHLineIteratorPixel it = createHLineIterator(bounds.x(), bounds.y(), bounds.width(), false); - for (int y2 = bounds.y(); y2 < bounds.height() - bounds.y(); ++y2) { - int x2 = 0; - while (!it.isDone()) { - TQ_UINT8 s = MAX_SELECTED - *(it.rawData()); - TQ_INT32 c = tqRgb(s, s, s); - img.setPixel(x2, y2, c); - ++x2; - ++it; - } - it.nextRow(); - } - return img; -} -void KisSelection::select(TQRect r) -{ - KisFillPainter painter(this); - KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); - painter.fillRect(r, KisColor(TQt::white, cs), MAX_SELECTED); - TQ_INT32 x, y, w, h; - extent(x, y, w, h); -} - -void KisSelection::clear(TQRect r) -{ - KisFillPainter painter(this); - KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); - painter.fillRect(r, KisColor(TQt::white, cs), MIN_SELECTED); -} - -void KisSelection::clear() -{ - TQ_UINT8 defPixel = MIN_SELECTED; - m_datamanager->setDefaultPixel(&defPixel); - m_datamanager->clear(); -} - -void KisSelection::invert() -{ - TQ_INT32 x,y,w,h; - - extent(x, y, w, h); - KisRectIterator it = createRectIterator(x, y, w, h, true); - while ( ! it.isDone() ) - { - // CBR this is wrong only first byte is inverted - // BSAR: But we have always only one byte in this color model :-). - *(it.rawData()) = MAX_SELECTED - *(it.rawData()); - ++it; - } - TQ_UINT8 defPixel = MAX_SELECTED - *(m_datamanager->defaultPixel()); - m_datamanager->setDefaultPixel(&defPixel); -} - -bool KisSelection::isTotallyUnselected(TQRect r) -{ - if(*(m_datamanager->defaultPixel()) != MIN_SELECTED) - return false; - TQRect sr = selectedExactRect(); - return ! r.intersects(sr); -} - -bool KisSelection::isProbablyTotallyUnselected(TQRect r) -{ - if(*(m_datamanager->defaultPixel()) != MIN_SELECTED) - return false; - TQRect sr = selectedRect(); - return ! r.intersects(sr); -} - - -TQRect KisSelection::selectedRect() const -{ - if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice) - return extent(); - else - return extent().unite(m_parentPaintDevice->extent()); -} - -TQRect KisSelection::selectedExactRect() const -{ - if(m_doCacheExactRect) - return m_cachedExactRect; - else if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice) - return exactBounds(); - else - return exactBounds().unite(m_parentPaintDevice->exactBounds()); -} - -void KisSelection::stopCachingExactRect() -{ - kdDebug() << "stop caching the exact rect" << endl; - m_doCacheExactRect = false; -} - - -void KisSelection::startCachingExactRect() -{ - kdDebug() << "start caching the exact rect" << endl; - if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice) - m_cachedExactRect = exactBounds(); - else - m_cachedExactRect = exactBounds().unite(m_parentPaintDevice->exactBounds()); - m_doCacheExactRect = true; -} - -void KisSelection::paintUniformSelectionRegion(TQImage img, const TQRect& imageRect, const TQRegion& uniformRegion) -{ - Q_ASSERT(img.size() == imageRect.size()); - Q_ASSERT(imageRect.contains(uniformRegion.boundingRect())); - - if (img.isNull() || img.size() != imageRect.size() || !imageRect.contains(uniformRegion.boundingRect())) { - return; - } - - if (*m_datamanager->defaultPixel() == MIN_SELECTED) { - - TQRegion region = uniformRegion & TQRegion(imageRect); - - if (!region.isEmpty()) { - TQMemArray rects = region.rects(); - - for (unsigned int i = 0; i < rects.count(); i++) { - TQRect r = rects[i]; - - for (TQ_INT32 y = 0; y < r.height(); ++y) { - - TQRgb *imagePixel = reinterpret_cast(img.scanLine(r.y() - imageRect.y() + y)); - imagePixel += r.x() - imageRect.x(); - - TQ_INT32 numPixels = r.width(); - - while (numPixels > 0) { - - TQRgb srcPixel = *imagePixel; - TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9; - TQ_UINT8 srcAlpha = tqAlpha(srcPixel); - - srcGrey = UINT8_MULT(srcGrey, srcAlpha); - TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192); - - TQRgb dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha); - *imagePixel = dstPixel; - - ++imagePixel; - --numPixels; - } - } - } - } - } -} - -void KisSelection::paintSelection(TQImage img, TQ_INT32 imageRectX, TQ_INT32 imageRectY, TQ_INT32 imageRectWidth, TQ_INT32 imageRectHeight) -{ - Q_ASSERT(img.size() == TQSize(imageRectWidth, imageRectHeight)); - - if (img.isNull() || img.size() != TQSize(imageRectWidth, imageRectHeight)) { - return; - } - - TQRect imageRect(imageRectX, imageRectY, imageRectWidth, imageRectHeight); - TQRect selectionExtent = extent(); - - selectionExtent.setLeft(selectionExtent.left() - 1); - selectionExtent.setTop(selectionExtent.top() - 1); - selectionExtent.setWidth(selectionExtent.width() + 2); - selectionExtent.setHeight(selectionExtent.height() + 2); - - TQRegion uniformRegion = TQRegion(imageRect); - uniformRegion -= TQRegion(selectionExtent); - - if (!uniformRegion.isEmpty()) { - paintUniformSelectionRegion(img, imageRect, uniformRegion); - } - - TQRect nonuniformRect = imageRect & selectionExtent; - - if (!nonuniformRect.isEmpty()) { - - const TQ_INT32 imageRectOffsetX = nonuniformRect.x() - imageRectX; - const TQ_INT32 imageRectOffsetY = nonuniformRect.y() - imageRectY; - - imageRectX = nonuniformRect.x(); - imageRectY = nonuniformRect.y(); - imageRectWidth = nonuniformRect.width(); - imageRectHeight = nonuniformRect.height(); - - const TQ_INT32 NUM_SELECTION_ROWS = 3; - - TQ_UINT8 *selectionRow[NUM_SELECTION_ROWS]; - - TQ_INT32 aboveRowIndex = 0; - TQ_INT32 centreRowIndex = 1; - TQ_INT32 belowRowIndex = 2; - - selectionRow[aboveRowIndex] = new TQ_UINT8[imageRectWidth + 2]; - selectionRow[centreRowIndex] = new TQ_UINT8[imageRectWidth + 2]; - selectionRow[belowRowIndex] = new TQ_UINT8[imageRectWidth + 2]; - - readBytes(selectionRow[centreRowIndex], imageRectX - 1, imageRectY - 1, imageRectWidth + 2, 1); - readBytes(selectionRow[belowRowIndex], imageRectX - 1, imageRectY, imageRectWidth + 2, 1); - - for (TQ_INT32 y = 0; y < imageRectHeight; ++y) { - - TQ_INT32 oldAboveRowIndex = aboveRowIndex; - aboveRowIndex = centreRowIndex; - centreRowIndex = belowRowIndex; - belowRowIndex = oldAboveRowIndex; - - readBytes(selectionRow[belowRowIndex], imageRectX - 1, imageRectY + y + 1, imageRectWidth + 2, 1); - - const TQ_UINT8 *aboveRow = selectionRow[aboveRowIndex] + 1; - const TQ_UINT8 *centreRow = selectionRow[centreRowIndex] + 1; - const TQ_UINT8 *belowRow = selectionRow[belowRowIndex] + 1; - - TQRgb *imagePixel = reinterpret_cast(img.scanLine(imageRectOffsetY + y)); - imagePixel += imageRectOffsetX; - - for (TQ_INT32 x = 0; x < imageRectWidth; ++x) { - - TQ_UINT8 centre = *centreRow; - - if (centre != MAX_SELECTED) { - - // this is where we come if the pixels should be blue or bluish - - TQRgb srcPixel = *imagePixel; - TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9; - TQ_UINT8 srcAlpha = tqAlpha(srcPixel); - - // Colour influence is proportional to alphaPixel. - srcGrey = UINT8_MULT(srcGrey, srcAlpha); - - TQRgb dstPixel; - - if (centre == MIN_SELECTED) { - //this is where we come if the pixels should be blue (or red outline) - - TQ_UINT8 left = *(centreRow - 1); - TQ_UINT8 right = *(centreRow + 1); - TQ_UINT8 above = *aboveRow; - TQ_UINT8 below = *belowRow; - - // Stop unselected transparent areas from appearing the same - // as selected transparent areas. - TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192); - - // now for a simple outline based on 4-connectivity - if (left != MIN_SELECTED || right != MIN_SELECTED || above != MIN_SELECTED || below != MIN_SELECTED) { - dstPixel = tqRgba(255, 0, 0, dstAlpha); - } else { - dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha); - } - } else { - dstPixel = tqRgba(UINT8_BLEND(tqRed(srcPixel), srcGrey + 128, centre), - UINT8_BLEND(tqGreen(srcPixel), srcGrey + 128, centre), - UINT8_BLEND(tqBlue(srcPixel), srcGrey + 165, centre), - srcAlpha); - } - - *imagePixel = dstPixel; - } - - aboveRow++; - centreRow++; - belowRow++; - imagePixel++; - } - } - - delete [] selectionRow[aboveRowIndex]; - delete [] selectionRow[centreRowIndex]; - delete [] selectionRow[belowRowIndex]; - } -} - -void KisSelection::paintSelection(TQImage img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize) -{ - if (img.isNull() || scaledImageRect.isEmpty() || scaledImageSize.isEmpty() || imageSize.isEmpty()) { - return; - } - - Q_ASSERT(img.size() == scaledImageRect.size()); - - if (img.size() != scaledImageRect.size()) { - return; - } - - TQ_INT32 imageWidth = imageSize.width(); - TQ_INT32 imageHeight = imageSize.height(); - - TQRect selectionExtent = extent(); - - selectionExtent.setLeft(selectionExtent.left() - 1); - selectionExtent.setTop(selectionExtent.top() - 1); - selectionExtent.setWidth(selectionExtent.width() + 2); - selectionExtent.setHeight(selectionExtent.height() + 2); - - double xScale = static_cast(scaledImageSize.width()) / imageWidth; - double yScale = static_cast(scaledImageSize.height()) / imageHeight; - - TQRect scaledSelectionExtent; - - scaledSelectionExtent.setLeft(static_cast(selectionExtent.left() * xScale)); - scaledSelectionExtent.setRight(static_cast(ceil((selectionExtent.right() + 1) * xScale)) - 1); - scaledSelectionExtent.setTop(static_cast(selectionExtent.top() * yScale)); - scaledSelectionExtent.setBottom(static_cast(ceil((selectionExtent.bottom() + 1) * yScale)) - 1); - - TQRegion uniformRegion = TQRegion(scaledImageRect); - uniformRegion -= TQRegion(scaledSelectionExtent); - - if (!uniformRegion.isEmpty()) { - paintUniformSelectionRegion(img, scaledImageRect, uniformRegion); - } - - TQRect nonuniformRect = scaledImageRect & scaledSelectionExtent; - - if (!nonuniformRect.isEmpty()) { - - const TQ_INT32 scaledImageRectXOffset = nonuniformRect.x() - scaledImageRect.x(); - const TQ_INT32 scaledImageRectYOffset = nonuniformRect.y() - scaledImageRect.y(); - - const TQ_INT32 scaledImageRectX = nonuniformRect.x(); - const TQ_INT32 scaledImageRectY = nonuniformRect.y(); - const TQ_INT32 scaledImageRectWidth = nonuniformRect.width(); - const TQ_INT32 scaledImageRectHeight = nonuniformRect.height(); - - const TQ_INT32 imageRowLeft = static_cast(scaledImageRectX / xScale); - const TQ_INT32 imageRowRight = static_cast((ceil((scaledImageRectX + scaledImageRectWidth - 1 + 1) / xScale)) - 1); - - const TQ_INT32 imageRowWidth = imageRowRight - imageRowLeft + 1; - const TQ_INT32 imageRowStride = imageRowWidth + 2; - - const TQ_INT32 NUM_SELECTION_ROWS = 3; - - TQ_INT32 aboveRowIndex = 0; - TQ_INT32 centreRowIndex = 1; - TQ_INT32 belowRowIndex = 2; - - TQ_INT32 aboveRowSrcY = -3; - TQ_INT32 centreRowSrcY = -3; - TQ_INT32 belowRowSrcY = -3; - - TQ_UINT8 *selectionRows = new TQ_UINT8[imageRowStride * NUM_SELECTION_ROWS]; - TQ_UINT8 *selectionRow[NUM_SELECTION_ROWS]; - - selectionRow[0] = selectionRows + 1; - selectionRow[1] = selectionRow[0] + imageRowStride; - selectionRow[2] = selectionRow[0] + (2 * imageRowStride); - - for (TQ_INT32 y = 0; y < scaledImageRectHeight; ++y) { - - TQ_INT32 scaledY = scaledImageRectY + y; - TQ_INT32 srcY = (scaledY * imageHeight) / scaledImageSize.height(); - - TQ_UINT8 *aboveRow; - TQ_UINT8 *centreRow; - TQ_UINT8 *belowRow; - - if (srcY - 1 == aboveRowSrcY) { - aboveRow = selectionRow[aboveRowIndex]; - centreRow = selectionRow[centreRowIndex]; - belowRow = selectionRow[belowRowIndex]; - } else if (srcY - 1 == centreRowSrcY) { - - TQ_INT32 oldAboveRowIndex = aboveRowIndex; - - aboveRowIndex = centreRowIndex; - centreRowIndex = belowRowIndex; - belowRowIndex = oldAboveRowIndex; - - aboveRow = selectionRow[aboveRowIndex]; - centreRow = selectionRow[centreRowIndex]; - belowRow = selectionRow[belowRowIndex]; - - readBytes(belowRow - 1, imageRowLeft - 1, srcY + 1, imageRowStride, 1); - - } else if (srcY - 1 == belowRowSrcY) { - - TQ_INT32 oldAboveRowIndex = aboveRowIndex; - TQ_INT32 oldCentreRowIndex = centreRowIndex; - - aboveRowIndex = belowRowIndex; - centreRowIndex = oldAboveRowIndex; - belowRowIndex = oldCentreRowIndex; - - aboveRow = selectionRow[aboveRowIndex]; - centreRow = selectionRow[centreRowIndex]; - belowRow = selectionRow[belowRowIndex]; - - if (belowRowIndex == centreRowIndex + 1) { - readBytes(centreRow - 1, imageRowLeft - 1, srcY, imageRowStride, 2); - } else { - readBytes(centreRow - 1, imageRowLeft - 1, srcY, imageRowStride, 1); - readBytes(belowRow - 1, imageRowLeft - 1, srcY + 1, imageRowStride, 1); - } - - } else { - - aboveRowIndex = 0; - centreRowIndex = 1; - belowRowIndex = 2; - - aboveRow = selectionRow[aboveRowIndex]; - centreRow = selectionRow[centreRowIndex]; - belowRow = selectionRow[belowRowIndex]; - - readBytes(selectionRows, imageRowLeft - 1, srcY - 1, imageRowStride, NUM_SELECTION_ROWS); - } - - aboveRowSrcY = srcY - 1; - centreRowSrcY = aboveRowSrcY + 1; - belowRowSrcY = centreRowSrcY + 1; - - TQRgb *imagePixel = reinterpret_cast(img.scanLine(scaledImageRectYOffset + y)); - imagePixel += scaledImageRectXOffset; - - for (TQ_INT32 x = 0; x < scaledImageRectWidth; ++x) { - - TQ_INT32 scaledX = scaledImageRectX + x; - TQ_INT32 srcX = (scaledX * imageWidth) / scaledImageSize.width(); - - TQ_UINT8 centre = *(centreRow + srcX - imageRowLeft); - - if (centre != MAX_SELECTED) { - - // this is where we come if the pixels should be blue or bluish - - TQRgb srcPixel = *imagePixel; - TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9; - TQ_UINT8 srcAlpha = tqAlpha(srcPixel); - - // Colour influence is proportional to alphaPixel. - srcGrey = UINT8_MULT(srcGrey, srcAlpha); - - TQRgb dstPixel; - - if (centre == MIN_SELECTED) { - //this is where we come if the pixels should be blue (or red outline) - - TQ_UINT8 left = *(centreRow + (srcX - imageRowLeft) - 1); - TQ_UINT8 right = *(centreRow + (srcX - imageRowLeft) + 1); - TQ_UINT8 above = *(aboveRow + (srcX - imageRowLeft)); - TQ_UINT8 below = *(belowRow + (srcX - imageRowLeft)); - - // Stop unselected transparent areas from appearing the same - // as selected transparent areas. - TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192); - - // now for a simple outline based on 4-connectivity - if (left != MIN_SELECTED || right != MIN_SELECTED || above != MIN_SELECTED || below != MIN_SELECTED) { - dstPixel = tqRgba(255, 0, 0, dstAlpha); - } else { - dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha); - } - } else { - dstPixel = tqRgba(UINT8_BLEND(tqRed(srcPixel), srcGrey + 128, centre), - UINT8_BLEND(tqGreen(srcPixel), srcGrey + 128, centre), - UINT8_BLEND(tqBlue(srcPixel), srcGrey + 165, centre), - srcAlpha); - } - - *imagePixel = dstPixel; - } - - imagePixel++; - } - } - - delete [] selectionRows; - } -} - -void KisSelection::setDirty(const TQRect& rc) -{ - if (m_dirty) - super::setDirty(rc); -} - -void KisSelection::setDirty() -{ - if (m_dirty) - super::setDirty(); -} diff --git a/chalk/core/kis_selection.cpp b/chalk/core/kis_selection.cpp new file mode 100644 index 00000000..ea8f2be2 --- /dev/null +++ b/chalk/core/kis_selection.cpp @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2004 Boudewijn Rempt + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. + */ + +#include + +#include +#include +#include + +#include "kis_layer.h" +#include "kis_debug_areas.h" +#include "kis_types.h" +#include "kis_colorspace_factory_registry.h" +#include "kis_fill_painter.h" +#include "kis_iterators_pixel.h" +#include "kis_integer_maths.h" +#include "kis_image.h" +#include "kis_datamanager.h" +#include "kis_fill_painter.h" +#include "kis_selection.h" + +KisSelection::KisSelection(KisPaintDeviceSP dev) + : super(dev->parentLayer() + , KisMetaRegistry::instance()->csRegistry()->getAlpha8() + , (TQString("selection for ") + dev->name()).latin1()) + , m_parentPaintDevice(dev) + , m_doCacheExactRect(false) + , m_dirty(false) +{ + Q_ASSERT(dev); +} + +KisSelection::KisSelection() + : super(KisMetaRegistry::instance()->csRegistry()->getAlpha8(), "anonymous selection") + , m_parentPaintDevice(0), m_dirty(false) +{ +} + +KisSelection::KisSelection(const KisSelection& rhs) + : super(rhs), m_parentPaintDevice(rhs.m_parentPaintDevice), m_doCacheExactRect(rhs.m_doCacheExactRect), + m_cachedExactRect(rhs.m_cachedExactRect), m_dirty(rhs.m_dirty) +{ +} + +KisSelection::~KisSelection() +{ +} + +TQ_UINT8 KisSelection::selected(TQ_INT32 x, TQ_INT32 y) +{ + KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false); + + TQ_UINT8 *pix = iter.rawData(); + + return *pix; +} + +void KisSelection::setSelected(TQ_INT32 x, TQ_INT32 y, TQ_UINT8 s) +{ + KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); + + TQ_UINT8 *pix = iter.rawData(); + + *pix = s; +} + +TQImage KisSelection::maskImage() +{ + // If part of a KisAdjustmentLayer, there may be no parent device. + TQImage img; + TQRect bounds; + if (m_parentPaintDevice) { + + bounds = m_parentPaintDevice->exactBounds(); + bounds = bounds.intersect( m_parentPaintDevice->image()->bounds() ); + img = TQImage(bounds.width(), bounds.height(), 32); + } + else { + bounds = TQRect( 0, 0, image()->width(), image()->height()); + img = TQImage(bounds.width(), bounds.height(), 32); + } + + KisHLineIteratorPixel it = createHLineIterator(bounds.x(), bounds.y(), bounds.width(), false); + for (int y2 = bounds.y(); y2 < bounds.height() - bounds.y(); ++y2) { + int x2 = 0; + while (!it.isDone()) { + TQ_UINT8 s = MAX_SELECTED - *(it.rawData()); + TQ_INT32 c = tqRgb(s, s, s); + img.setPixel(x2, y2, c); + ++x2; + ++it; + } + it.nextRow(); + } + return img; +} +void KisSelection::select(TQRect r) +{ + KisFillPainter painter(this); + KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); + painter.fillRect(r, KisColor(TQt::white, cs), MAX_SELECTED); + TQ_INT32 x, y, w, h; + extent(x, y, w, h); +} + +void KisSelection::clear(TQRect r) +{ + KisFillPainter painter(this); + KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8(); + painter.fillRect(r, KisColor(TQt::white, cs), MIN_SELECTED); +} + +void KisSelection::clear() +{ + TQ_UINT8 defPixel = MIN_SELECTED; + m_datamanager->setDefaultPixel(&defPixel); + m_datamanager->clear(); +} + +void KisSelection::invert() +{ + TQ_INT32 x,y,w,h; + + extent(x, y, w, h); + KisRectIterator it = createRectIterator(x, y, w, h, true); + while ( ! it.isDone() ) + { + // CBR this is wrong only first byte is inverted + // BSAR: But we have always only one byte in this color model :-). + *(it.rawData()) = MAX_SELECTED - *(it.rawData()); + ++it; + } + TQ_UINT8 defPixel = MAX_SELECTED - *(m_datamanager->defaultPixel()); + m_datamanager->setDefaultPixel(&defPixel); +} + +bool KisSelection::isTotallyUnselected(TQRect r) +{ + if(*(m_datamanager->defaultPixel()) != MIN_SELECTED) + return false; + TQRect sr = selectedExactRect(); + return ! r.intersects(sr); +} + +bool KisSelection::isProbablyTotallyUnselected(TQRect r) +{ + if(*(m_datamanager->defaultPixel()) != MIN_SELECTED) + return false; + TQRect sr = selectedRect(); + return ! r.intersects(sr); +} + + +TQRect KisSelection::selectedRect() const +{ + if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice) + return extent(); + else + return extent().unite(m_parentPaintDevice->extent()); +} + +TQRect KisSelection::selectedExactRect() const +{ + if(m_doCacheExactRect) + return m_cachedExactRect; + else if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice) + return exactBounds(); + else + return exactBounds().unite(m_parentPaintDevice->exactBounds()); +} + +void KisSelection::stopCachingExactRect() +{ + kdDebug() << "stop caching the exact rect" << endl; + m_doCacheExactRect = false; +} + + +void KisSelection::startCachingExactRect() +{ + kdDebug() << "start caching the exact rect" << endl; + if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice) + m_cachedExactRect = exactBounds(); + else + m_cachedExactRect = exactBounds().unite(m_parentPaintDevice->exactBounds()); + m_doCacheExactRect = true; +} + +void KisSelection::paintUniformSelectionRegion(TQImage img, const TQRect& imageRect, const TQRegion& uniformRegion) +{ + Q_ASSERT(img.size() == imageRect.size()); + Q_ASSERT(imageRect.contains(uniformRegion.boundingRect())); + + if (img.isNull() || img.size() != imageRect.size() || !imageRect.contains(uniformRegion.boundingRect())) { + return; + } + + if (*m_datamanager->defaultPixel() == MIN_SELECTED) { + + TQRegion region = uniformRegion & TQRegion(imageRect); + + if (!region.isEmpty()) { + TQMemArray rects = region.rects(); + + for (unsigned int i = 0; i < rects.count(); i++) { + TQRect r = rects[i]; + + for (TQ_INT32 y = 0; y < r.height(); ++y) { + + TQRgb *imagePixel = reinterpret_cast(img.scanLine(r.y() - imageRect.y() + y)); + imagePixel += r.x() - imageRect.x(); + + TQ_INT32 numPixels = r.width(); + + while (numPixels > 0) { + + TQRgb srcPixel = *imagePixel; + TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9; + TQ_UINT8 srcAlpha = tqAlpha(srcPixel); + + srcGrey = UINT8_MULT(srcGrey, srcAlpha); + TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192); + + TQRgb dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha); + *imagePixel = dstPixel; + + ++imagePixel; + --numPixels; + } + } + } + } + } +} + +void KisSelection::paintSelection(TQImage img, TQ_INT32 imageRectX, TQ_INT32 imageRectY, TQ_INT32 imageRectWidth, TQ_INT32 imageRectHeight) +{ + Q_ASSERT(img.size() == TQSize(imageRectWidth, imageRectHeight)); + + if (img.isNull() || img.size() != TQSize(imageRectWidth, imageRectHeight)) { + return; + } + + TQRect imageRect(imageRectX, imageRectY, imageRectWidth, imageRectHeight); + TQRect selectionExtent = extent(); + + selectionExtent.setLeft(selectionExtent.left() - 1); + selectionExtent.setTop(selectionExtent.top() - 1); + selectionExtent.setWidth(selectionExtent.width() + 2); + selectionExtent.setHeight(selectionExtent.height() + 2); + + TQRegion uniformRegion = TQRegion(imageRect); + uniformRegion -= TQRegion(selectionExtent); + + if (!uniformRegion.isEmpty()) { + paintUniformSelectionRegion(img, imageRect, uniformRegion); + } + + TQRect nonuniformRect = imageRect & selectionExtent; + + if (!nonuniformRect.isEmpty()) { + + const TQ_INT32 imageRectOffsetX = nonuniformRect.x() - imageRectX; + const TQ_INT32 imageRectOffsetY = nonuniformRect.y() - imageRectY; + + imageRectX = nonuniformRect.x(); + imageRectY = nonuniformRect.y(); + imageRectWidth = nonuniformRect.width(); + imageRectHeight = nonuniformRect.height(); + + const TQ_INT32 NUM_SELECTION_ROWS = 3; + + TQ_UINT8 *selectionRow[NUM_SELECTION_ROWS]; + + TQ_INT32 aboveRowIndex = 0; + TQ_INT32 centreRowIndex = 1; + TQ_INT32 belowRowIndex = 2; + + selectionRow[aboveRowIndex] = new TQ_UINT8[imageRectWidth + 2]; + selectionRow[centreRowIndex] = new TQ_UINT8[imageRectWidth + 2]; + selectionRow[belowRowIndex] = new TQ_UINT8[imageRectWidth + 2]; + + readBytes(selectionRow[centreRowIndex], imageRectX - 1, imageRectY - 1, imageRectWidth + 2, 1); + readBytes(selectionRow[belowRowIndex], imageRectX - 1, imageRectY, imageRectWidth + 2, 1); + + for (TQ_INT32 y = 0; y < imageRectHeight; ++y) { + + TQ_INT32 oldAboveRowIndex = aboveRowIndex; + aboveRowIndex = centreRowIndex; + centreRowIndex = belowRowIndex; + belowRowIndex = oldAboveRowIndex; + + readBytes(selectionRow[belowRowIndex], imageRectX - 1, imageRectY + y + 1, imageRectWidth + 2, 1); + + const TQ_UINT8 *aboveRow = selectionRow[aboveRowIndex] + 1; + const TQ_UINT8 *centreRow = selectionRow[centreRowIndex] + 1; + const TQ_UINT8 *belowRow = selectionRow[belowRowIndex] + 1; + + TQRgb *imagePixel = reinterpret_cast(img.scanLine(imageRectOffsetY + y)); + imagePixel += imageRectOffsetX; + + for (TQ_INT32 x = 0; x < imageRectWidth; ++x) { + + TQ_UINT8 centre = *centreRow; + + if (centre != MAX_SELECTED) { + + // this is where we come if the pixels should be blue or bluish + + TQRgb srcPixel = *imagePixel; + TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9; + TQ_UINT8 srcAlpha = tqAlpha(srcPixel); + + // Colour influence is proportional to alphaPixel. + srcGrey = UINT8_MULT(srcGrey, srcAlpha); + + TQRgb dstPixel; + + if (centre == MIN_SELECTED) { + //this is where we come if the pixels should be blue (or red outline) + + TQ_UINT8 left = *(centreRow - 1); + TQ_UINT8 right = *(centreRow + 1); + TQ_UINT8 above = *aboveRow; + TQ_UINT8 below = *belowRow; + + // Stop unselected transparent areas from appearing the same + // as selected transparent areas. + TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192); + + // now for a simple outline based on 4-connectivity + if (left != MIN_SELECTED || right != MIN_SELECTED || above != MIN_SELECTED || below != MIN_SELECTED) { + dstPixel = tqRgba(255, 0, 0, dstAlpha); + } else { + dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha); + } + } else { + dstPixel = tqRgba(UINT8_BLEND(tqRed(srcPixel), srcGrey + 128, centre), + UINT8_BLEND(tqGreen(srcPixel), srcGrey + 128, centre), + UINT8_BLEND(tqBlue(srcPixel), srcGrey + 165, centre), + srcAlpha); + } + + *imagePixel = dstPixel; + } + + aboveRow++; + centreRow++; + belowRow++; + imagePixel++; + } + } + + delete [] selectionRow[aboveRowIndex]; + delete [] selectionRow[centreRowIndex]; + delete [] selectionRow[belowRowIndex]; + } +} + +void KisSelection::paintSelection(TQImage img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize) +{ + if (img.isNull() || scaledImageRect.isEmpty() || scaledImageSize.isEmpty() || imageSize.isEmpty()) { + return; + } + + Q_ASSERT(img.size() == scaledImageRect.size()); + + if (img.size() != scaledImageRect.size()) { + return; + } + + TQ_INT32 imageWidth = imageSize.width(); + TQ_INT32 imageHeight = imageSize.height(); + + TQRect selectionExtent = extent(); + + selectionExtent.setLeft(selectionExtent.left() - 1); + selectionExtent.setTop(selectionExtent.top() - 1); + selectionExtent.setWidth(selectionExtent.width() + 2); + selectionExtent.setHeight(selectionExtent.height() + 2); + + double xScale = static_cast(scaledImageSize.width()) / imageWidth; + double yScale = static_cast(scaledImageSize.height()) / imageHeight; + + TQRect scaledSelectionExtent; + + scaledSelectionExtent.setLeft(static_cast(selectionExtent.left() * xScale)); + scaledSelectionExtent.setRight(static_cast(ceil((selectionExtent.right() + 1) * xScale)) - 1); + scaledSelectionExtent.setTop(static_cast(selectionExtent.top() * yScale)); + scaledSelectionExtent.setBottom(static_cast(ceil((selectionExtent.bottom() + 1) * yScale)) - 1); + + TQRegion uniformRegion = TQRegion(scaledImageRect); + uniformRegion -= TQRegion(scaledSelectionExtent); + + if (!uniformRegion.isEmpty()) { + paintUniformSelectionRegion(img, scaledImageRect, uniformRegion); + } + + TQRect nonuniformRect = scaledImageRect & scaledSelectionExtent; + + if (!nonuniformRect.isEmpty()) { + + const TQ_INT32 scaledImageRectXOffset = nonuniformRect.x() - scaledImageRect.x(); + const TQ_INT32 scaledImageRectYOffset = nonuniformRect.y() - scaledImageRect.y(); + + const TQ_INT32 scaledImageRectX = nonuniformRect.x(); + const TQ_INT32 scaledImageRectY = nonuniformRect.y(); + const TQ_INT32 scaledImageRectWidth = nonuniformRect.width(); + const TQ_INT32 scaledImageRectHeight = nonuniformRect.height(); + + const TQ_INT32 imageRowLeft = static_cast(scaledImageRectX / xScale); + const TQ_INT32 imageRowRight = static_cast((ceil((scaledImageRectX + scaledImageRectWidth - 1 + 1) / xScale)) - 1); + + const TQ_INT32 imageRowWidth = imageRowRight - imageRowLeft + 1; + const TQ_INT32 imageRowStride = imageRowWidth + 2; + + const TQ_INT32 NUM_SELECTION_ROWS = 3; + + TQ_INT32 aboveRowIndex = 0; + TQ_INT32 centreRowIndex = 1; + TQ_INT32 belowRowIndex = 2; + + TQ_INT32 aboveRowSrcY = -3; + TQ_INT32 centreRowSrcY = -3; + TQ_INT32 belowRowSrcY = -3; + + TQ_UINT8 *selectionRows = new TQ_UINT8[imageRowStride * NUM_SELECTION_ROWS]; + TQ_UINT8 *selectionRow[NUM_SELECTION_ROWS]; + + selectionRow[0] = selectionRows + 1; + selectionRow[1] = selectionRow[0] + imageRowStride; + selectionRow[2] = selectionRow[0] + (2 * imageRowStride); + + for (TQ_INT32 y = 0; y < scaledImageRectHeight; ++y) { + + TQ_INT32 scaledY = scaledImageRectY + y; + TQ_INT32 srcY = (scaledY * imageHeight) / scaledImageSize.height(); + + TQ_UINT8 *aboveRow; + TQ_UINT8 *centreRow; + TQ_UINT8 *belowRow; + + if (srcY - 1 == aboveRowSrcY) { + aboveRow = selectionRow[aboveRowIndex]; + centreRow = selectionRow[centreRowIndex]; + belowRow = selectionRow[belowRowIndex]; + } else if (srcY - 1 == centreRowSrcY) { + + TQ_INT32 oldAboveRowIndex = aboveRowIndex; + + aboveRowIndex = centreRowIndex; + centreRowIndex = belowRowIndex; + belowRowIndex = oldAboveRowIndex; + + aboveRow = selectionRow[aboveRowIndex]; + centreRow = selectionRow[centreRowIndex]; + belowRow = selectionRow[belowRowIndex]; + + readBytes(belowRow - 1, imageRowLeft - 1, srcY + 1, imageRowStride, 1); + + } else if (srcY - 1 == belowRowSrcY) { + + TQ_INT32 oldAboveRowIndex = aboveRowIndex; + TQ_INT32 oldCentreRowIndex = centreRowIndex; + + aboveRowIndex = belowRowIndex; + centreRowIndex = oldAboveRowIndex; + belowRowIndex = oldCentreRowIndex; + + aboveRow = selectionRow[aboveRowIndex]; + centreRow = selectionRow[centreRowIndex]; + belowRow = selectionRow[belowRowIndex]; + + if (belowRowIndex == centreRowIndex + 1) { + readBytes(centreRow - 1, imageRowLeft - 1, srcY, imageRowStride, 2); + } else { + readBytes(centreRow - 1, imageRowLeft - 1, srcY, imageRowStride, 1); + readBytes(belowRow - 1, imageRowLeft - 1, srcY + 1, imageRowStride, 1); + } + + } else { + + aboveRowIndex = 0; + centreRowIndex = 1; + belowRowIndex = 2; + + aboveRow = selectionRow[aboveRowIndex]; + centreRow = selectionRow[centreRowIndex]; + belowRow = selectionRow[belowRowIndex]; + + readBytes(selectionRows, imageRowLeft - 1, srcY - 1, imageRowStride, NUM_SELECTION_ROWS); + } + + aboveRowSrcY = srcY - 1; + centreRowSrcY = aboveRowSrcY + 1; + belowRowSrcY = centreRowSrcY + 1; + + TQRgb *imagePixel = reinterpret_cast(img.scanLine(scaledImageRectYOffset + y)); + imagePixel += scaledImageRectXOffset; + + for (TQ_INT32 x = 0; x < scaledImageRectWidth; ++x) { + + TQ_INT32 scaledX = scaledImageRectX + x; + TQ_INT32 srcX = (scaledX * imageWidth) / scaledImageSize.width(); + + TQ_UINT8 centre = *(centreRow + srcX - imageRowLeft); + + if (centre != MAX_SELECTED) { + + // this is where we come if the pixels should be blue or bluish + + TQRgb srcPixel = *imagePixel; + TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9; + TQ_UINT8 srcAlpha = tqAlpha(srcPixel); + + // Colour influence is proportional to alphaPixel. + srcGrey = UINT8_MULT(srcGrey, srcAlpha); + + TQRgb dstPixel; + + if (centre == MIN_SELECTED) { + //this is where we come if the pixels should be blue (or red outline) + + TQ_UINT8 left = *(centreRow + (srcX - imageRowLeft) - 1); + TQ_UINT8 right = *(centreRow + (srcX - imageRowLeft) + 1); + TQ_UINT8 above = *(aboveRow + (srcX - imageRowLeft)); + TQ_UINT8 below = *(belowRow + (srcX - imageRowLeft)); + + // Stop unselected transparent areas from appearing the same + // as selected transparent areas. + TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192); + + // now for a simple outline based on 4-connectivity + if (left != MIN_SELECTED || right != MIN_SELECTED || above != MIN_SELECTED || below != MIN_SELECTED) { + dstPixel = tqRgba(255, 0, 0, dstAlpha); + } else { + dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha); + } + } else { + dstPixel = tqRgba(UINT8_BLEND(tqRed(srcPixel), srcGrey + 128, centre), + UINT8_BLEND(tqGreen(srcPixel), srcGrey + 128, centre), + UINT8_BLEND(tqBlue(srcPixel), srcGrey + 165, centre), + srcAlpha); + } + + *imagePixel = dstPixel; + } + + imagePixel++; + } + } + + delete [] selectionRows; + } +} + +void KisSelection::setDirty(const TQRect& rc) +{ + if (m_dirty) + super::setDirty(rc); +} + +void KisSelection::setDirty() +{ + if (m_dirty) + super::setDirty(); +} diff --git a/chalk/core/kis_strategy_move.cc b/chalk/core/kis_strategy_move.cc deleted file mode 100644 index d5102c8b..00000000 --- a/chalk/core/kis_strategy_move.cc +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include - -#include -#include -#include -#include - -#include "kis_canvas_controller.h" -#include "kis_canvas_subject.h" -#include "kis_image.h" -#include "kis_layer.h" -#include "kis_strategy_move.h" -#include "kis_undo_adapter.h" - -KisStrategyMove::KisStrategyMove() -{ - reset(0); -} - -KisStrategyMove::KisStrategyMove(KisCanvasSubject *subject) -{ - reset(subject); -} - -KisStrategyMove::~KisStrategyMove() -{ -} - -void KisStrategyMove::reset(KisCanvasSubject *subject) -{ - m_subject = subject; - m_dragging = false; - - if (m_subject) { - m_controller = subject->canvasController(); - } else { - m_controller = 0; - } -} - -void KisStrategyMove::startDrag(const TQPoint& pos) -{ - // pos is the user chosen handle point - - if (m_subject) { - KisImageSP img; - KisLayerSP dev; - - if (!(img = m_subject->currentImg())) - return; - - dev = img->activeLayer(); - - if (!dev || !dev->visible()) - return; - - m_dragging = true; - m_dragStart.setX(pos.x()); - m_dragStart.setY(pos.y()); - m_layerStart.setX(dev->x()); - m_layerStart.setY(dev->y()); - m_layerPosition = m_layerStart; - } -} - -void KisStrategyMove::drag(const TQPoint& original) -{ - // original is the position of the user chosen handle point - - if (m_subject && m_dragging) { - KisImageSP img = m_subject->currentImg(); - KisLayerSP dev; - - if (img && (dev = img->activeLayer())) { - TQPoint pos = original; - TQRect rc; - - pos -= m_dragStart; // convert to delta - rc = dev->extent(); - dev->setX(dev->x() + pos.x()); - dev->setY(dev->y() + pos.y()); - rc = rc.unite(dev->extent()); - - m_layerPosition = TQPoint(dev->x(), dev->y()); - m_dragStart = original; - - dev->setDirty(rc); - } - } -} - -void KisStrategyMove::endDrag(const TQPoint& pos, bool undo) -{ - if (m_subject && m_dragging) { - KisImageSP img = m_subject->currentImg(); - KisLayerSP dev; - - if (img && (dev = img->activeLayer())) { - drag(pos); - m_dragging = false; - - if (undo && img->undo()) { - KCommand *cmd = dev->moveCommand(m_layerStart, m_layerPosition); - TQ_CHECK_PTR(cmd); - - KisUndoAdapter *adapter = img->undoAdapter(); - if (adapter) { - adapter->addCommand(cmd); - } else { - delete cmd; - } - } - img->setModified(); - } - } -} - -void KisStrategyMove::simpleMove(const TQPoint& pt1, const TQPoint& pt2) -{ - startDrag(pt1); - endDrag(pt2); -} - -void KisStrategyMove::simpleMove(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 x2, TQ_INT32 y2) -{ - startDrag(TQPoint(x1, y1)); - endDrag(TQPoint(x2, y2)); -} - diff --git a/chalk/core/kis_strategy_move.cpp b/chalk/core/kis_strategy_move.cpp new file mode 100644 index 00000000..d5102c8b --- /dev/null +++ b/chalk/core/kis_strategy_move.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2002 Patrick Julien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include +#include +#include +#include + +#include "kis_canvas_controller.h" +#include "kis_canvas_subject.h" +#include "kis_image.h" +#include "kis_layer.h" +#include "kis_strategy_move.h" +#include "kis_undo_adapter.h" + +KisStrategyMove::KisStrategyMove() +{ + reset(0); +} + +KisStrategyMove::KisStrategyMove(KisCanvasSubject *subject) +{ + reset(subject); +} + +KisStrategyMove::~KisStrategyMove() +{ +} + +void KisStrategyMove::reset(KisCanvasSubject *subject) +{ + m_subject = subject; + m_dragging = false; + + if (m_subject) { + m_controller = subject->canvasController(); + } else { + m_controller = 0; + } +} + +void KisStrategyMove::startDrag(const TQPoint& pos) +{ + // pos is the user chosen handle point + + if (m_subject) { + KisImageSP img; + KisLayerSP dev; + + if (!(img = m_subject->currentImg())) + return; + + dev = img->activeLayer(); + + if (!dev || !dev->visible()) + return; + + m_dragging = true; + m_dragStart.setX(pos.x()); + m_dragStart.setY(pos.y()); + m_layerStart.setX(dev->x()); + m_layerStart.setY(dev->y()); + m_layerPosition = m_layerStart; + } +} + +void KisStrategyMove::drag(const TQPoint& original) +{ + // original is the position of the user chosen handle point + + if (m_subject && m_dragging) { + KisImageSP img = m_subject->currentImg(); + KisLayerSP dev; + + if (img && (dev = img->activeLayer())) { + TQPoint pos = original; + TQRect rc; + + pos -= m_dragStart; // convert to delta + rc = dev->extent(); + dev->setX(dev->x() + pos.x()); + dev->setY(dev->y() + pos.y()); + rc = rc.unite(dev->extent()); + + m_layerPosition = TQPoint(dev->x(), dev->y()); + m_dragStart = original; + + dev->setDirty(rc); + } + } +} + +void KisStrategyMove::endDrag(const TQPoint& pos, bool undo) +{ + if (m_subject && m_dragging) { + KisImageSP img = m_subject->currentImg(); + KisLayerSP dev; + + if (img && (dev = img->activeLayer())) { + drag(pos); + m_dragging = false; + + if (undo && img->undo()) { + KCommand *cmd = dev->moveCommand(m_layerStart, m_layerPosition); + TQ_CHECK_PTR(cmd); + + KisUndoAdapter *adapter = img->undoAdapter(); + if (adapter) { + adapter->addCommand(cmd); + } else { + delete cmd; + } + } + img->setModified(); + } + } +} + +void KisStrategyMove::simpleMove(const TQPoint& pt1, const TQPoint& pt2) +{ + startDrag(pt1); + endDrag(pt2); +} + +void KisStrategyMove::simpleMove(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 x2, TQ_INT32 y2) +{ + startDrag(TQPoint(x1, y1)); + endDrag(TQPoint(x2, y2)); +} + diff --git a/chalk/core/kis_thread_pool.cc b/chalk/core/kis_thread_pool.cc deleted file mode 100644 index 12807192..00000000 --- a/chalk/core/kis_thread_pool.cc +++ /dev/null @@ -1,192 +0,0 @@ -/* - * copyright (c) 2006 Boudewijn Rempt - * - * This program is free software; you can distribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "kis_thread_pool.h" -#include -#include -#include - -KisThreadPool * KisThreadPool::m_singleton = 0; - -KisThreadPool::KisThreadPool() -{ - Q_ASSERT(KisThreadPool::m_singleton == 0); - - KisThreadPool::m_singleton = this; - - TDEConfig * cfg = TDEGlobal::config(); - cfg->setGroup(""); - m_maxThreads = cfg->readNumEntry("maxthreads", 10); - m_numberOfRunningThreads = 0; - m_numberOfQueuedThreads = 0; - m_wait = 200; - - start(); -} - - -KisThreadPool::~KisThreadPool() -{ - m_poolMutex.lock(); - - m_canceled = true; - - m_runningThreads.setAutoDelete(true); - m_threads.setAutoDelete(true); - m_oldThreads.setAutoDelete(true); - - KisThread * t; - - for ( t = m_threads.first(); t; t = m_threads.next()) { - if (t) { - t->cancel(); - t->wait(); - m_threads.remove(t); - } - } - - for ( t = m_runningThreads.first(); t; t = m_runningThreads.next()) { - if (t) { - t->cancel(); - t->wait(); - m_runningThreads.remove(t); - } - } - - for ( t = m_oldThreads.first(); t; t = m_oldThreads.next()) { - if (t) { - t->cancel(); - t->wait(); - m_runningThreads.remove(t); - } - } - KisThreadPool::m_singleton = 0; - m_poolMutex.unlock(); - -} - - -KisThreadPool * KisThreadPool::instance() -{ - if(KisThreadPool::m_singleton == 0) - { - KisThreadPool::m_singleton = new KisThreadPool(); - } - else { - - if (KisThreadPool::m_singleton->finished()) { - delete KisThreadPool::m_singleton; - KisThreadPool::m_singleton = 0; - KisThreadPool::m_singleton = new KisThreadPool(); - } - } - - return KisThreadPool::m_singleton; -} - -void KisThreadPool::enqueue(KisThread * thread) -{ - m_poolMutex.lock(); - m_threads.append(thread); - m_numberOfQueuedThreads++; - m_poolMutex.unlock(); - m_wait = 200; -} - - -void KisThreadPool::dequeue(KisThread * thread) -{ - KisThread * t = 0; - - m_poolMutex.lock(); - - int i = m_threads.findRef(thread); - if (i >= 0) { - t = m_threads.take(i); - m_numberOfQueuedThreads--; - } else { - i = m_runningThreads.findRef(thread); - if (i >= 0) { - t = m_runningThreads.take(i); - m_numberOfRunningThreads--; - } - else { - i = m_oldThreads.findRef(thread); - if (i >= 0) { - t = m_oldThreads.take(i); - } - } - } - - m_poolMutex.unlock(); - - if (t) { - t->cancel(); - t->wait(); - delete t; - } - -} - -void KisThreadPool::run() -{ - int sleeps = 10; - - while(!m_canceled) { - if (m_numberOfQueuedThreads > 0 && m_numberOfRunningThreads < m_maxThreads) { - KisThread * thread = 0; - m_poolMutex.lock(); - if (m_threads.count() > 0) { - thread = m_threads.take(); - m_numberOfQueuedThreads--; - } - if (thread) { - thread->start(); - m_runningThreads.append(thread); - m_numberOfRunningThreads++; - } - m_poolMutex.unlock(); - } - else { - msleep(m_wait); - m_poolMutex.lock(); - for ( KisThread * t = m_runningThreads.first(); t; t = m_runningThreads.next()) { - if (t) { - if (t->finished()) { - m_runningThreads.remove(t); - m_numberOfRunningThreads--; - m_oldThreads.append(t); - } - } - } - m_poolMutex.unlock(); - m_poolMutex.lock(); - if (m_numberOfQueuedThreads == 0 && m_numberOfRunningThreads == 0) { - sleeps--; - if (sleeps == 0) { - m_poolMutex.unlock(); - return; - } - m_poolMutex.unlock(); - - } - m_poolMutex.unlock(); - - } - } -} diff --git a/chalk/core/kis_thread_pool.cpp b/chalk/core/kis_thread_pool.cpp new file mode 100644 index 00000000..12807192 --- /dev/null +++ b/chalk/core/kis_thread_pool.cpp @@ -0,0 +1,192 @@ +/* + * copyright (c) 2006 Boudewijn Rempt + * + * This program is free software; you can distribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_thread_pool.h" +#include +#include +#include + +KisThreadPool * KisThreadPool::m_singleton = 0; + +KisThreadPool::KisThreadPool() +{ + Q_ASSERT(KisThreadPool::m_singleton == 0); + + KisThreadPool::m_singleton = this; + + TDEConfig * cfg = TDEGlobal::config(); + cfg->setGroup(""); + m_maxThreads = cfg->readNumEntry("maxthreads", 10); + m_numberOfRunningThreads = 0; + m_numberOfQueuedThreads = 0; + m_wait = 200; + + start(); +} + + +KisThreadPool::~KisThreadPool() +{ + m_poolMutex.lock(); + + m_canceled = true; + + m_runningThreads.setAutoDelete(true); + m_threads.setAutoDelete(true); + m_oldThreads.setAutoDelete(true); + + KisThread * t; + + for ( t = m_threads.first(); t; t = m_threads.next()) { + if (t) { + t->cancel(); + t->wait(); + m_threads.remove(t); + } + } + + for ( t = m_runningThreads.first(); t; t = m_runningThreads.next()) { + if (t) { + t->cancel(); + t->wait(); + m_runningThreads.remove(t); + } + } + + for ( t = m_oldThreads.first(); t; t = m_oldThreads.next()) { + if (t) { + t->cancel(); + t->wait(); + m_runningThreads.remove(t); + } + } + KisThreadPool::m_singleton = 0; + m_poolMutex.unlock(); + +} + + +KisThreadPool * KisThreadPool::instance() +{ + if(KisThreadPool::m_singleton == 0) + { + KisThreadPool::m_singleton = new KisThreadPool(); + } + else { + + if (KisThreadPool::m_singleton->finished()) { + delete KisThreadPool::m_singleton; + KisThreadPool::m_singleton = 0; + KisThreadPool::m_singleton = new KisThreadPool(); + } + } + + return KisThreadPool::m_singleton; +} + +void KisThreadPool::enqueue(KisThread * thread) +{ + m_poolMutex.lock(); + m_threads.append(thread); + m_numberOfQueuedThreads++; + m_poolMutex.unlock(); + m_wait = 200; +} + + +void KisThreadPool::dequeue(KisThread * thread) +{ + KisThread * t = 0; + + m_poolMutex.lock(); + + int i = m_threads.findRef(thread); + if (i >= 0) { + t = m_threads.take(i); + m_numberOfQueuedThreads--; + } else { + i = m_runningThreads.findRef(thread); + if (i >= 0) { + t = m_runningThreads.take(i); + m_numberOfRunningThreads--; + } + else { + i = m_oldThreads.findRef(thread); + if (i >= 0) { + t = m_oldThreads.take(i); + } + } + } + + m_poolMutex.unlock(); + + if (t) { + t->cancel(); + t->wait(); + delete t; + } + +} + +void KisThreadPool::run() +{ + int sleeps = 10; + + while(!m_canceled) { + if (m_numberOfQueuedThreads > 0 && m_numberOfRunningThreads < m_maxThreads) { + KisThread * thread = 0; + m_poolMutex.lock(); + if (m_threads.count() > 0) { + thread = m_threads.take(); + m_numberOfQueuedThreads--; + } + if (thread) { + thread->start(); + m_runningThreads.append(thread); + m_numberOfRunningThreads++; + } + m_poolMutex.unlock(); + } + else { + msleep(m_wait); + m_poolMutex.lock(); + for ( KisThread * t = m_runningThreads.first(); t; t = m_runningThreads.next()) { + if (t) { + if (t->finished()) { + m_runningThreads.remove(t); + m_numberOfRunningThreads--; + m_oldThreads.append(t); + } + } + } + m_poolMutex.unlock(); + m_poolMutex.lock(); + if (m_numberOfQueuedThreads == 0 && m_numberOfRunningThreads == 0) { + sleeps--; + if (sleeps == 0) { + m_poolMutex.unlock(); + return; + } + m_poolMutex.unlock(); + + } + m_poolMutex.unlock(); + + } + } +} diff --git a/chalk/core/kis_transaction.cc b/chalk/core/kis_transaction.cc deleted file mode 100644 index d0a10acc..00000000 --- a/chalk/core/kis_transaction.cc +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_types.h" -#include "kis_global.h" -#include "kis_tile.h" -#include "kis_tileddatamanager.h" -#include "kis_image.h" -#include "kis_transaction.h" -#include "kis_memento.h" -#include "kis_paint_device.h" -#include "kis_layer.h" - -class KisTransactionPrivate { -public: - TQString m_name; - KisPaintDeviceSP m_device; - KisMementoSP m_memento; - -}; - -KisTransaction::KisTransaction(const TQString& name, KisPaintDeviceSP device) -{ - m_private = new KisTransactionPrivate; - - m_private->m_name = name; - m_private->m_device = device; - m_private->m_memento = device->getMemento(); -} - -KisTransaction::~KisTransaction() -{ - if (m_private->m_memento) { - // For debugging purposes - m_private->m_memento->setInvalid(); - } - delete m_private; -} - -void KisTransaction::execute() -{ - Q_ASSERT(m_private->m_memento != 0); - - m_private->m_device->rollforward(m_private->m_memento); - - TQRect rc; - TQ_INT32 x, y, width, height; - m_private->m_memento->extent(x,y,width,height); - rc.setRect(x + m_private->m_device->getX(), y + m_private->m_device->getY(), width, height); - - KisLayerSP l = m_private->m_device->parentLayer(); - if (l) l->setDirty(rc); -} - -void KisTransaction::unexecute() -{ - Q_ASSERT(m_private->m_memento != 0); - m_private->m_device->rollback(m_private->m_memento); - - TQRect rc; - TQ_INT32 x, y, width, height; - m_private->m_memento->extent(x,y,width,height); - rc.setRect(x + m_private->m_device->getX(), y + m_private->m_device->getY(), width, height); - - KisLayerSP l = m_private->m_device->parentLayer(); - if (l) l->setDirty(rc); - -} - -void KisTransaction::unexecuteNoUpdate() -{ - Q_ASSERT(m_private->m_memento != 0); - - m_private->m_device->rollback(m_private->m_memento); -} - -TQString KisTransaction::name() const -{ - return m_private->m_name; -} diff --git a/chalk/core/kis_transaction.cpp b/chalk/core/kis_transaction.cpp new file mode 100644 index 00000000..d0a10acc --- /dev/null +++ b/chalk/core/kis_transaction.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2002 Patrick Julien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_types.h" +#include "kis_global.h" +#include "kis_tile.h" +#include "kis_tileddatamanager.h" +#include "kis_image.h" +#include "kis_transaction.h" +#include "kis_memento.h" +#include "kis_paint_device.h" +#include "kis_layer.h" + +class KisTransactionPrivate { +public: + TQString m_name; + KisPaintDeviceSP m_device; + KisMementoSP m_memento; + +}; + +KisTransaction::KisTransaction(const TQString& name, KisPaintDeviceSP device) +{ + m_private = new KisTransactionPrivate; + + m_private->m_name = name; + m_private->m_device = device; + m_private->m_memento = device->getMemento(); +} + +KisTransaction::~KisTransaction() +{ + if (m_private->m_memento) { + // For debugging purposes + m_private->m_memento->setInvalid(); + } + delete m_private; +} + +void KisTransaction::execute() +{ + Q_ASSERT(m_private->m_memento != 0); + + m_private->m_device->rollforward(m_private->m_memento); + + TQRect rc; + TQ_INT32 x, y, width, height; + m_private->m_memento->extent(x,y,width,height); + rc.setRect(x + m_private->m_device->getX(), y + m_private->m_device->getY(), width, height); + + KisLayerSP l = m_private->m_device->parentLayer(); + if (l) l->setDirty(rc); +} + +void KisTransaction::unexecute() +{ + Q_ASSERT(m_private->m_memento != 0); + m_private->m_device->rollback(m_private->m_memento); + + TQRect rc; + TQ_INT32 x, y, width, height; + m_private->m_memento->extent(x,y,width,height); + rc.setRect(x + m_private->m_device->getX(), y + m_private->m_device->getY(), width, height); + + KisLayerSP l = m_private->m_device->parentLayer(); + if (l) l->setDirty(rc); + +} + +void KisTransaction::unexecuteNoUpdate() +{ + Q_ASSERT(m_private->m_memento != 0); + + m_private->m_device->rollback(m_private->m_memento); +} + +TQString KisTransaction::name() const +{ + return m_private->m_name; +} diff --git a/chalk/core/kis_transform_worker.cc b/chalk/core/kis_transform_worker.cc deleted file mode 100644 index 0d4a98b4..00000000 --- a/chalk/core/kis_transform_worker.cc +++ /dev/null @@ -1,676 +0,0 @@ -/* - * Copyright (c) 2004 Michael Thaler filters - * Copyright (c) 2005 Casper Boemann - * Copyright (c) 2005 Boudewijn Rempt right angle rotators - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include - -#include "kis_debug_areas.h" -#include "kis_paint_device.h" -#include "kis_selection.h" -#include "kis_transform_worker.h" -#include "kis_progress_display_interface.h" -#include "kis_iterators_pixel.h" -#include "kis_filter_strategy.h" -#include "kis_layer.h" -#include "kis_painter.h" - -KisTransformWorker::KisTransformWorker(KisPaintDeviceSP dev, double xscale, double yscale, - double xshear, double yshear, double rotation, - TQ_INT32 xtranslate, TQ_INT32 ytranslate, - KisProgressDisplayInterface *progress, KisFilterStrategy *filter, bool fixBorderAlpha) -{ - m_dev= dev; - m_xscale = xscale; - m_yscale = yscale; - m_xshear = xshear; - m_yshear = yshear; - m_rotation = rotation, - m_xtranslate = xtranslate; - m_ytranslate = ytranslate; - m_progress = progress; - m_filter = filter; - m_fixBorderAlpha = fixBorderAlpha; -} - -void KisTransformWorker::rotateNone(KisPaintDeviceSP src, KisPaintDeviceSP dst) -{ - KisSelectionSP dstSelection; - TQ_INT32 pixelSize = src->pixelSize(); - TQRect r; - KisColorSpace *cs = src->colorSpace(); - - if(src->hasSelection()) - { - r = src->selection()->selectedExactRect(); - dstSelection = dst->selection(); - } - else - { - r = src->exactBounds(); - dstSelection = new KisSelection(dst); // essentially a dummy to be deleted - } - - KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), r.top(), r.width(), true); - KisHLineIterator vit = dst->createHLineIterator(r.x(), r.top(), r.width(), true); - KisHLineIterator dstSelIt = dstSelection->createHLineIterator(r.x(), r.top(), r.width(), true); - for (TQ_INT32 i = 0; i < r.height(); ++i) { - while (!hit.isDone()) { - if (hit.isSelected()) { - memcpy(vit.rawData(), hit.rawData(), pixelSize); - - // XXX: Should set alpha = alpha*(1-selectedness) - cs->setAlpha(hit.rawData(), 0, 1); - } - *(dstSelIt.rawData()) = hit.selectedness(); - ++hit; - ++vit; - ++dstSelIt; - } - hit.nextRow(); - vit.nextRow(); - dstSelIt.nextRow(); - - //progress info - m_progressStep += r.width(); - if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) - { - m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; - emit notifyProgress(m_lastProgressReport); - } - if (m_cancelRequested) { - break; - } - } -} - -void KisTransformWorker::rotateRight90(KisPaintDeviceSP src, KisPaintDeviceSP dst) -{ - KisSelectionSP dstSelection; - TQ_INT32 pixelSize = src->pixelSize(); - TQRect r; - KisColorSpace *cs = src->colorSpace(); - - if(src->hasSelection()) - { - r = src->selection()->selectedExactRect(); - dstSelection = dst->selection(); - } - else - { - r = src->exactBounds(); - dstSelection = new KisSelection(dst); // essentially a dummy to be deleted - } - - for (TQ_INT32 y = r.bottom(); y >= r.top(); --y) { - KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), true); - KisVLineIterator vit = dst->createVLineIterator(-y, r.x(), r.width(), true); - KisVLineIterator dstSelIt = dstSelection->createVLineIterator(-y, r.x(), r.width(), true); - - while (!hit.isDone()) { - if (hit.isSelected()) { - memcpy(vit.rawData(), hit.rawData(), pixelSize); - - // XXX: Should set alpha = alpha*(1-selectedness) - cs->setAlpha(hit.rawData(), 0, 1); - } - *(dstSelIt.rawData()) = hit.selectedness(); - ++hit; - ++vit; - ++dstSelIt; - } - - //progress info - m_progressStep += r.width(); - if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) - { - m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; - emit notifyProgress(m_lastProgressReport); - } - if (m_cancelRequested) { - break; - } - } -} - -void KisTransformWorker::rotateLeft90(KisPaintDeviceSP src, KisPaintDeviceSP dst) -{ - kdDebug() << "rotateLeft90 called\n"; - KisSelectionSP dstSelection; - TQ_INT32 pixelSize = src->pixelSize(); - TQRect r; - KisColorSpace *cs = src->colorSpace(); - - if(src->hasSelection()) - { - r = src->selection()->selectedExactRect(); - dstSelection = dst->selection(); - } - else - { - r = src->exactBounds(); - dstSelection = new KisSelection(dst); // essentially a dummy to be deleted - } - TQ_INT32 x = 0; - - for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { - // Read the horizontal line from back to front, write onto the vertical column - KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), true); - KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true); - KisVLineIterator dstSelIt = dstSelection->createVLineIterator(y, -r.x() - r.width(), r.width(), true); - - hit += r.width() - 1; - while (!vit.isDone()) { - if (hit.isSelected()) { - memcpy(vit.rawData(), hit.rawData(), pixelSize); - - // XXX: Should set alpha = alpha*(1-selectedness) - cs->setAlpha(hit.rawData(), 0, 1); - } - *(dstSelIt.rawData()) = hit.selectedness(); - --hit; - ++vit; - ++dstSelIt; - } - ++x; - - //progress info - m_progressStep += r.width(); - if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) - { - m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; - emit notifyProgress(m_lastProgressReport); - } - if (m_cancelRequested) { - break; - } - } -} - -void KisTransformWorker::rotate180(KisPaintDeviceSP src, KisPaintDeviceSP dst) -{ - kdDebug() << "Rotating 180\n"; - KisSelectionSP dstSelection; - TQ_INT32 pixelSize = src->pixelSize(); - TQRect r; - KisColorSpace *cs = src->colorSpace(); - - if(src->hasSelection()) - { - r = src->selection()->selectedExactRect(); - dstSelection = dst->selection(); - } - else - { - r = src->exactBounds(); - dstSelection = new KisSelection(dst); // essentially a dummy to be deleted - } - - for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { - KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), true); - KisHLineIterator dstIt = dst->createHLineIterator(-r.x() - r.width(), -y, r.width(), true); - KisHLineIterator dstSelIt = dstSelection->createHLineIterator(-r.x() - r.width(), -y, r.width(), true); - - srcIt += r.width() - 1; - while (!dstIt.isDone()) { - if (srcIt.isSelected()) { - memcpy(dstIt.rawData(), srcIt.rawData(), pixelSize); - - // XXX: Should set alpha = alpha*(1-selectedness) - cs->setAlpha(srcIt.rawData(), 0, 1); - } - *(dstSelIt.rawData()) = srcIt.selectedness(); - --srcIt; - ++dstIt; - ++dstSelIt; - } - - //progress info - m_progressStep += r.width(); - if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) - { - m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; - emit notifyProgress(m_lastProgressReport); - } - if (m_cancelRequested) { - break; - } - } -} - -template iter createIterator(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len); - -template <> KisHLineIteratorPixel createIterator -(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len) -{ - return dev->createHLineIterator(start, lineNum, len, true); -} - -template <> KisVLineIteratorPixel createIterator -(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len) -{ - return dev->createVLineIterator(lineNum, start, len, true); -} - -template void calcDimensions (KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines); - -template <> void calcDimensions -(KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines) -{ - if(dev->hasSelection()) - { - TQRect r = dev->selection()->selectedExactRect(); - r.rect(&srcStart, &firstLine, &srcLen, &numLines); - } - else - dev->exactBounds(srcStart, firstLine, srcLen, numLines); -} - -template <> void calcDimensions -(KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines) -{ - if(dev->hasSelection()) - { - TQRect r = dev->selection()->selectedExactRect(); - r.rect(&firstLine, &srcStart, &numLines, &srcLen); - } - else - dev->exactBounds(firstLine, srcStart, numLines, srcLen); -} - -struct FilterValues -{ - TQ_UINT8 numWeights; - TQ_UINT8 *weight; - ~FilterValues() {delete [] weight;} -}; - -template void KisTransformWorker::transformPass(KisPaintDevice *src, KisPaintDevice *dst, double floatscale, double shear, TQ_INT32 dx, KisFilterStrategy *filterStrategy, bool fixBorderAlpha) -{ - TQ_INT32 lineNum,srcStart,firstLine,srcLen,numLines; - TQ_INT32 center, begin, end; /* filter calculation variables */ - TQ_UINT8 *data; - TQ_UINT8 pixelSize = src->pixelSize(); - KisSelectionSP dstSelection; - KisColorSpace * cs = src->colorSpace(); - TQ_INT32 scale; - TQ_INT32 scaleDenom; - TQ_INT32 shearFracOffset; - - if(src->hasSelection()) - dstSelection = dst->selection(); - else - dstSelection = new KisSelection(dst); // essentially a dummy to be deleted - - calcDimensions (src, srcStart, srcLen, firstLine, numLines); - - scale = int(floatscale*srcLen); - scaleDenom = srcLen; - - if(scaleDenom == 0) - return; - - TQ_INT32 support = filterStrategy->intSupport(); - TQ_INT32 dstLen, dstStart; - TQ_INT32 invfscale = 256; - - // handle magnification/minification - if(abs(scale) < scaleDenom) - { - support *= scaleDenom; - support /= scale; - - invfscale *= scale; - invfscale /= scaleDenom; - if(scale < 0) // handle mirroring - { - support = -support; - invfscale = -invfscale; - } - } - - // handle mirroring - if(scale < 0) - dstLen = - scale; - else - dstLen = scale; - - // Calculate extra length (in each side) needed due to shear - TQ_INT32 extraLen = (support+256)>>8 + 1; - - TQ_UINT8 *tmpLine = new TQ_UINT8[(srcLen +2*extraLen)* pixelSize]; - TQ_CHECK_PTR(tmpLine); - - TQ_UINT8 *tmpSel = new TQ_UINT8[srcLen+2*extraLen]; - TQ_CHECK_PTR(tmpSel); - - //allocate space for colors - const TQ_UINT8 **colors = new const TQ_UINT8 *[2*support+1]; - - // Precalculate weights - FilterValues *filterWeights = new FilterValues[256]; - - for(int center = 0; center<256; ++center) - { - TQ_INT32 begin = (255 + center - support)>>8; // takes ceiling by adding 255 - TQ_INT32 span = ((center + support)>>8) - begin + 1; // takes floor to get end. Subtracts begin to get span - TQ_INT32 t = (((begin<<8) - center) * invfscale)>>8; - TQ_INT32 dt = invfscale; - filterWeights[center].weight = new TQ_UINT8[span]; -//printf("%d (",center); - TQ_UINT32 sum=0; - for(int num = 0; numintValueAt(t) * invfscale; - - tmpw >>=8; - filterWeights[center].weight[num] = tmpw; -//printf(" %d=%d,%d",t,filterWeights[center].weight[num],tmpw); - t += dt; - sum+=tmpw; - } -//printf(" )%d sum =%d",span,sum); - if(sum!=255) - { - double fixfactor= 255.0/sum; - sum=0; - for(int num = 0; num(src, srcStart - extraLen, lineNum, srcLen+2*extraLen); - TQ_INT32 i = 0; - while(!srcIt.isDone()) - { - TQ_UINT8 *data; - - data = srcIt.rawData(); - memcpy(&tmpLine[i*pixelSize], data, pixelSize); - - if(srcIt.isSelected()) - { - // XXX: Should set alpha = alpha*(1-selectedness) - cs->setAlpha(data, 0, 1); - tmpSel[i] = 255; - } - else { - tmpSel[i] = 0; - } - ++srcIt; - i++; - } - - T dstIt = createIterator (dst, dstStart, lineNum, dstLen); - T dstSelIt = createIterator (dstSelection, dstStart, lineNum, dstLen); - - i=0; - while(!dstIt.isDone()) - { - if(scaleDenom<2500) - center = ((i<<8) * scaleDenom) / scale; - else - { - if(scaleDenom<46000) // real limit is actually 46340 pixels - center = ((i * scaleDenom) / scale)<<8; - else - center = ((i<<8)/scale * scaleDenom) / scale; // XXX fails for sizes over 2^23 pixels src width - } - - if(scale < 0) - center += srcLen<<8; - - center += 128*scaleDenom/scale;//xxx doesn't work for scale<0; - center += (extraLen<<8) + shearFracOffset; - - // find contributing pixels - begin = (255 + center - support)>>8; // takes ceiling by adding 255 - end = (center + support)>>8; // takes floor - -////printf("sup=%d begin=%d end=%d",support,begin,end); - TQ_UINT8 selectedness = tmpSel[center>>8]; - if(selectedness) - { - int num=0; - for(int srcpos = begin; srcpos <= end; ++srcpos) - { - colors[num] = &tmpLine[srcpos*pixelSize]; - num++; - } - data = dstIt.rawData(); - cs->mixColors(colors, filterWeights[center&255].weight, filterWeights[center&255].numWeights, data); - - //possibly fix the alpha of the border if user wants it - if(fixBorderAlpha && (i==0 || i==dstLen-1)) - cs->setAlpha(data, cs->getAlpha(&tmpLine[(center>>8)*pixelSize]), 1); - - data = dstSelIt.rawData(); - *data = selectedness; - } - - ++dstSelIt; - ++dstIt; - i++; - } - - //progress info - m_progressStep += dstLen; - if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) - { - m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; - emit notifyProgress(m_lastProgressReport); - } - if (m_cancelRequested) { - break; - } - } - delete [] colors; - delete [] tmpLine; - delete [] tmpSel; - delete [] filterWeights; -} - -bool KisTransformWorker::run() -{ - //progress info - m_cancelRequested = false; - if(m_progress) - m_progress->setSubject(this, true, true); - m_progressTotalSteps = 0; - m_progressStep = 0; - TQRect r; - if(m_dev->hasSelection()) - r = m_dev->selection()->selectedExactRect(); - else - r = m_dev->exactBounds(); - - KisPaintDeviceSP tmpdev1 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev1");; - KisPaintDeviceSP tmpdev2 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");; - KisPaintDeviceSP tmpdev3 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");; - KisPaintDeviceSP srcdev = m_dev; - - double xscale = m_xscale; - double yscale = m_yscale; - double xshear = m_xshear; - double yshear = m_yshear; - double rotation = m_rotation; - TQ_INT32 xtranslate = m_xtranslate; - TQ_INT32 ytranslate = m_ytranslate; - - if(rotation < 0.0) - rotation = -fmod(-rotation, 2*M_PI) + 2*M_PI; - else - rotation = fmod(rotation, 2*M_PI); - int rotQuadrant = int(rotation /(M_PI/2) + 0.5) & 3; - - // Figure out how we will do the initial right angle rotations - double tmp; - switch(rotQuadrant) - { - default: // just to shut up the compiler - case 0: - m_progressTotalSteps = 0; - break; - case 1: - rotation -= M_PI/2; - tmp = xscale; - xscale=yscale; - yscale=tmp; - m_progressTotalSteps = r.width() * r.height(); - break; - case 2: - rotation -= M_PI; - m_progressTotalSteps = r.width() * r.height(); - break; - case 3: - rotation -= -M_PI/2 + 2*M_PI; - tmp = xscale; - xscale = yscale; - yscale = tmp; - m_progressTotalSteps = r.width() * r.height(); - break; - } - - // Calculate some auxillary values - yshear = sin(rotation); - xshear = -tan(rotation/2); - xtranslate -= int(xshear*ytranslate); - - // Calculate progress steps - m_progressTotalSteps += int(yscale * r.width() * r.height()); - m_progressTotalSteps += int(xscale * r.width() * (r.height() * yscale + r.width()*yshear)); - - m_lastProgressReport=0; - - // Now that we have everything in place it's time to do the actual right angle rotations - switch(rotQuadrant) - { - default: // just to shut up the compiler - case 0: - break; - case 1: - rotateRight90(srcdev, tmpdev1); - srcdev = tmpdev1; - break; - case 2: - rotate180(srcdev, tmpdev1); - srcdev = tmpdev1; - break; - case 3: - rotateLeft90(srcdev, tmpdev1); - srcdev = tmpdev1; - break; - } - - // Handle simple move case possibly with rotation of 90,180,270 - if(rotation == 0.0 && xscale == 1.0 && yscale == 1.0) - { - if(rotQuadrant==0) - { - // Though not nessesay in the general case because we make several passes - // We need to move (not just copy) the data to a temp dev so we can move them back - rotateNone(srcdev, tmpdev1); - srcdev = tmpdev1; - } - if(m_dev->hasSelection()) - m_dev->selection()->clear(); - - srcdev->move(srcdev->getX() + xtranslate, srcdev->getY() + ytranslate); - rotateNone(srcdev, m_dev); - - //progress info - emit notifyProgressDone(); - m_dev->emitSelectionChanged(); - - return m_cancelRequested; - } - - if ( m_cancelRequested) { - emit notifyProgressDone(); - return false; - } - - transformPass (srcdev, tmpdev2, xscale, yscale*xshear, 0, m_filter, m_fixBorderAlpha); - if(m_dev->hasSelection()) - m_dev->selection()->clear(); - - if ( m_cancelRequested) { - emit notifyProgressDone(); - return false; - } - - // Now do the second pass - transformPass (tmpdev2.data(), tmpdev3.data(), yscale, yshear, ytranslate, m_filter, m_fixBorderAlpha); - - if(m_dev->hasSelection()) - m_dev->selection()->clear(); - - if ( m_cancelRequested) { - emit notifyProgressDone(); - return false; - } - - if (xshear != 0.0) - transformPass (tmpdev3, m_dev, 1.0, xshear, xtranslate, m_filter, m_fixBorderAlpha); - else - { - // No need to filter again when we are only scaling - tmpdev3->move(tmpdev3->getX() + xtranslate, tmpdev3->getY()); - rotateNone(tmpdev3, m_dev); - } - - if (m_dev->parentLayer()) { - m_dev->parentLayer()->setDirty(); - } - //progress info - emit notifyProgressDone(); - m_dev->emitSelectionChanged(); - - return m_cancelRequested; -} diff --git a/chalk/core/kis_transform_worker.cpp b/chalk/core/kis_transform_worker.cpp new file mode 100644 index 00000000..0d4a98b4 --- /dev/null +++ b/chalk/core/kis_transform_worker.cpp @@ -0,0 +1,676 @@ +/* + * Copyright (c) 2004 Michael Thaler filters + * Copyright (c) 2005 Casper Boemann + * Copyright (c) 2005 Boudewijn Rempt right angle rotators + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include + +#include "kis_debug_areas.h" +#include "kis_paint_device.h" +#include "kis_selection.h" +#include "kis_transform_worker.h" +#include "kis_progress_display_interface.h" +#include "kis_iterators_pixel.h" +#include "kis_filter_strategy.h" +#include "kis_layer.h" +#include "kis_painter.h" + +KisTransformWorker::KisTransformWorker(KisPaintDeviceSP dev, double xscale, double yscale, + double xshear, double yshear, double rotation, + TQ_INT32 xtranslate, TQ_INT32 ytranslate, + KisProgressDisplayInterface *progress, KisFilterStrategy *filter, bool fixBorderAlpha) +{ + m_dev= dev; + m_xscale = xscale; + m_yscale = yscale; + m_xshear = xshear; + m_yshear = yshear; + m_rotation = rotation, + m_xtranslate = xtranslate; + m_ytranslate = ytranslate; + m_progress = progress; + m_filter = filter; + m_fixBorderAlpha = fixBorderAlpha; +} + +void KisTransformWorker::rotateNone(KisPaintDeviceSP src, KisPaintDeviceSP dst) +{ + KisSelectionSP dstSelection; + TQ_INT32 pixelSize = src->pixelSize(); + TQRect r; + KisColorSpace *cs = src->colorSpace(); + + if(src->hasSelection()) + { + r = src->selection()->selectedExactRect(); + dstSelection = dst->selection(); + } + else + { + r = src->exactBounds(); + dstSelection = new KisSelection(dst); // essentially a dummy to be deleted + } + + KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), r.top(), r.width(), true); + KisHLineIterator vit = dst->createHLineIterator(r.x(), r.top(), r.width(), true); + KisHLineIterator dstSelIt = dstSelection->createHLineIterator(r.x(), r.top(), r.width(), true); + for (TQ_INT32 i = 0; i < r.height(); ++i) { + while (!hit.isDone()) { + if (hit.isSelected()) { + memcpy(vit.rawData(), hit.rawData(), pixelSize); + + // XXX: Should set alpha = alpha*(1-selectedness) + cs->setAlpha(hit.rawData(), 0, 1); + } + *(dstSelIt.rawData()) = hit.selectedness(); + ++hit; + ++vit; + ++dstSelIt; + } + hit.nextRow(); + vit.nextRow(); + dstSelIt.nextRow(); + + //progress info + m_progressStep += r.width(); + if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) + { + m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; + emit notifyProgress(m_lastProgressReport); + } + if (m_cancelRequested) { + break; + } + } +} + +void KisTransformWorker::rotateRight90(KisPaintDeviceSP src, KisPaintDeviceSP dst) +{ + KisSelectionSP dstSelection; + TQ_INT32 pixelSize = src->pixelSize(); + TQRect r; + KisColorSpace *cs = src->colorSpace(); + + if(src->hasSelection()) + { + r = src->selection()->selectedExactRect(); + dstSelection = dst->selection(); + } + else + { + r = src->exactBounds(); + dstSelection = new KisSelection(dst); // essentially a dummy to be deleted + } + + for (TQ_INT32 y = r.bottom(); y >= r.top(); --y) { + KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), true); + KisVLineIterator vit = dst->createVLineIterator(-y, r.x(), r.width(), true); + KisVLineIterator dstSelIt = dstSelection->createVLineIterator(-y, r.x(), r.width(), true); + + while (!hit.isDone()) { + if (hit.isSelected()) { + memcpy(vit.rawData(), hit.rawData(), pixelSize); + + // XXX: Should set alpha = alpha*(1-selectedness) + cs->setAlpha(hit.rawData(), 0, 1); + } + *(dstSelIt.rawData()) = hit.selectedness(); + ++hit; + ++vit; + ++dstSelIt; + } + + //progress info + m_progressStep += r.width(); + if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) + { + m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; + emit notifyProgress(m_lastProgressReport); + } + if (m_cancelRequested) { + break; + } + } +} + +void KisTransformWorker::rotateLeft90(KisPaintDeviceSP src, KisPaintDeviceSP dst) +{ + kdDebug() << "rotateLeft90 called\n"; + KisSelectionSP dstSelection; + TQ_INT32 pixelSize = src->pixelSize(); + TQRect r; + KisColorSpace *cs = src->colorSpace(); + + if(src->hasSelection()) + { + r = src->selection()->selectedExactRect(); + dstSelection = dst->selection(); + } + else + { + r = src->exactBounds(); + dstSelection = new KisSelection(dst); // essentially a dummy to be deleted + } + TQ_INT32 x = 0; + + for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { + // Read the horizontal line from back to front, write onto the vertical column + KisHLineIteratorPixel hit = src->createHLineIterator(r.x(), y, r.width(), true); + KisVLineIterator vit = dst->createVLineIterator(y, -r.x() - r.width(), r.width(), true); + KisVLineIterator dstSelIt = dstSelection->createVLineIterator(y, -r.x() - r.width(), r.width(), true); + + hit += r.width() - 1; + while (!vit.isDone()) { + if (hit.isSelected()) { + memcpy(vit.rawData(), hit.rawData(), pixelSize); + + // XXX: Should set alpha = alpha*(1-selectedness) + cs->setAlpha(hit.rawData(), 0, 1); + } + *(dstSelIt.rawData()) = hit.selectedness(); + --hit; + ++vit; + ++dstSelIt; + } + ++x; + + //progress info + m_progressStep += r.width(); + if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) + { + m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; + emit notifyProgress(m_lastProgressReport); + } + if (m_cancelRequested) { + break; + } + } +} + +void KisTransformWorker::rotate180(KisPaintDeviceSP src, KisPaintDeviceSP dst) +{ + kdDebug() << "Rotating 180\n"; + KisSelectionSP dstSelection; + TQ_INT32 pixelSize = src->pixelSize(); + TQRect r; + KisColorSpace *cs = src->colorSpace(); + + if(src->hasSelection()) + { + r = src->selection()->selectedExactRect(); + dstSelection = dst->selection(); + } + else + { + r = src->exactBounds(); + dstSelection = new KisSelection(dst); // essentially a dummy to be deleted + } + + for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) { + KisHLineIteratorPixel srcIt = src->createHLineIterator(r.x(), y, r.width(), true); + KisHLineIterator dstIt = dst->createHLineIterator(-r.x() - r.width(), -y, r.width(), true); + KisHLineIterator dstSelIt = dstSelection->createHLineIterator(-r.x() - r.width(), -y, r.width(), true); + + srcIt += r.width() - 1; + while (!dstIt.isDone()) { + if (srcIt.isSelected()) { + memcpy(dstIt.rawData(), srcIt.rawData(), pixelSize); + + // XXX: Should set alpha = alpha*(1-selectedness) + cs->setAlpha(srcIt.rawData(), 0, 1); + } + *(dstSelIt.rawData()) = srcIt.selectedness(); + --srcIt; + ++dstIt; + ++dstSelIt; + } + + //progress info + m_progressStep += r.width(); + if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) + { + m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; + emit notifyProgress(m_lastProgressReport); + } + if (m_cancelRequested) { + break; + } + } +} + +template iter createIterator(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len); + +template <> KisHLineIteratorPixel createIterator +(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len) +{ + return dev->createHLineIterator(start, lineNum, len, true); +} + +template <> KisVLineIteratorPixel createIterator +(KisPaintDevice *dev, TQ_INT32 start, TQ_INT32 lineNum, TQ_INT32 len) +{ + return dev->createVLineIterator(lineNum, start, len, true); +} + +template void calcDimensions (KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines); + +template <> void calcDimensions +(KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines) +{ + if(dev->hasSelection()) + { + TQRect r = dev->selection()->selectedExactRect(); + r.rect(&srcStart, &firstLine, &srcLen, &numLines); + } + else + dev->exactBounds(srcStart, firstLine, srcLen, numLines); +} + +template <> void calcDimensions +(KisPaintDevice *dev, TQ_INT32 &srcStart, TQ_INT32 &srcLen, TQ_INT32 &firstLine, TQ_INT32 &numLines) +{ + if(dev->hasSelection()) + { + TQRect r = dev->selection()->selectedExactRect(); + r.rect(&firstLine, &srcStart, &numLines, &srcLen); + } + else + dev->exactBounds(firstLine, srcStart, numLines, srcLen); +} + +struct FilterValues +{ + TQ_UINT8 numWeights; + TQ_UINT8 *weight; + ~FilterValues() {delete [] weight;} +}; + +template void KisTransformWorker::transformPass(KisPaintDevice *src, KisPaintDevice *dst, double floatscale, double shear, TQ_INT32 dx, KisFilterStrategy *filterStrategy, bool fixBorderAlpha) +{ + TQ_INT32 lineNum,srcStart,firstLine,srcLen,numLines; + TQ_INT32 center, begin, end; /* filter calculation variables */ + TQ_UINT8 *data; + TQ_UINT8 pixelSize = src->pixelSize(); + KisSelectionSP dstSelection; + KisColorSpace * cs = src->colorSpace(); + TQ_INT32 scale; + TQ_INT32 scaleDenom; + TQ_INT32 shearFracOffset; + + if(src->hasSelection()) + dstSelection = dst->selection(); + else + dstSelection = new KisSelection(dst); // essentially a dummy to be deleted + + calcDimensions (src, srcStart, srcLen, firstLine, numLines); + + scale = int(floatscale*srcLen); + scaleDenom = srcLen; + + if(scaleDenom == 0) + return; + + TQ_INT32 support = filterStrategy->intSupport(); + TQ_INT32 dstLen, dstStart; + TQ_INT32 invfscale = 256; + + // handle magnification/minification + if(abs(scale) < scaleDenom) + { + support *= scaleDenom; + support /= scale; + + invfscale *= scale; + invfscale /= scaleDenom; + if(scale < 0) // handle mirroring + { + support = -support; + invfscale = -invfscale; + } + } + + // handle mirroring + if(scale < 0) + dstLen = - scale; + else + dstLen = scale; + + // Calculate extra length (in each side) needed due to shear + TQ_INT32 extraLen = (support+256)>>8 + 1; + + TQ_UINT8 *tmpLine = new TQ_UINT8[(srcLen +2*extraLen)* pixelSize]; + TQ_CHECK_PTR(tmpLine); + + TQ_UINT8 *tmpSel = new TQ_UINT8[srcLen+2*extraLen]; + TQ_CHECK_PTR(tmpSel); + + //allocate space for colors + const TQ_UINT8 **colors = new const TQ_UINT8 *[2*support+1]; + + // Precalculate weights + FilterValues *filterWeights = new FilterValues[256]; + + for(int center = 0; center<256; ++center) + { + TQ_INT32 begin = (255 + center - support)>>8; // takes ceiling by adding 255 + TQ_INT32 span = ((center + support)>>8) - begin + 1; // takes floor to get end. Subtracts begin to get span + TQ_INT32 t = (((begin<<8) - center) * invfscale)>>8; + TQ_INT32 dt = invfscale; + filterWeights[center].weight = new TQ_UINT8[span]; +//printf("%d (",center); + TQ_UINT32 sum=0; + for(int num = 0; numintValueAt(t) * invfscale; + + tmpw >>=8; + filterWeights[center].weight[num] = tmpw; +//printf(" %d=%d,%d",t,filterWeights[center].weight[num],tmpw); + t += dt; + sum+=tmpw; + } +//printf(" )%d sum =%d",span,sum); + if(sum!=255) + { + double fixfactor= 255.0/sum; + sum=0; + for(int num = 0; num(src, srcStart - extraLen, lineNum, srcLen+2*extraLen); + TQ_INT32 i = 0; + while(!srcIt.isDone()) + { + TQ_UINT8 *data; + + data = srcIt.rawData(); + memcpy(&tmpLine[i*pixelSize], data, pixelSize); + + if(srcIt.isSelected()) + { + // XXX: Should set alpha = alpha*(1-selectedness) + cs->setAlpha(data, 0, 1); + tmpSel[i] = 255; + } + else { + tmpSel[i] = 0; + } + ++srcIt; + i++; + } + + T dstIt = createIterator (dst, dstStart, lineNum, dstLen); + T dstSelIt = createIterator (dstSelection, dstStart, lineNum, dstLen); + + i=0; + while(!dstIt.isDone()) + { + if(scaleDenom<2500) + center = ((i<<8) * scaleDenom) / scale; + else + { + if(scaleDenom<46000) // real limit is actually 46340 pixels + center = ((i * scaleDenom) / scale)<<8; + else + center = ((i<<8)/scale * scaleDenom) / scale; // XXX fails for sizes over 2^23 pixels src width + } + + if(scale < 0) + center += srcLen<<8; + + center += 128*scaleDenom/scale;//xxx doesn't work for scale<0; + center += (extraLen<<8) + shearFracOffset; + + // find contributing pixels + begin = (255 + center - support)>>8; // takes ceiling by adding 255 + end = (center + support)>>8; // takes floor + +////printf("sup=%d begin=%d end=%d",support,begin,end); + TQ_UINT8 selectedness = tmpSel[center>>8]; + if(selectedness) + { + int num=0; + for(int srcpos = begin; srcpos <= end; ++srcpos) + { + colors[num] = &tmpLine[srcpos*pixelSize]; + num++; + } + data = dstIt.rawData(); + cs->mixColors(colors, filterWeights[center&255].weight, filterWeights[center&255].numWeights, data); + + //possibly fix the alpha of the border if user wants it + if(fixBorderAlpha && (i==0 || i==dstLen-1)) + cs->setAlpha(data, cs->getAlpha(&tmpLine[(center>>8)*pixelSize]), 1); + + data = dstSelIt.rawData(); + *data = selectedness; + } + + ++dstSelIt; + ++dstIt; + i++; + } + + //progress info + m_progressStep += dstLen; + if(m_lastProgressReport != (m_progressStep * 100) / m_progressTotalSteps) + { + m_lastProgressReport = (m_progressStep * 100) / m_progressTotalSteps; + emit notifyProgress(m_lastProgressReport); + } + if (m_cancelRequested) { + break; + } + } + delete [] colors; + delete [] tmpLine; + delete [] tmpSel; + delete [] filterWeights; +} + +bool KisTransformWorker::run() +{ + //progress info + m_cancelRequested = false; + if(m_progress) + m_progress->setSubject(this, true, true); + m_progressTotalSteps = 0; + m_progressStep = 0; + TQRect r; + if(m_dev->hasSelection()) + r = m_dev->selection()->selectedExactRect(); + else + r = m_dev->exactBounds(); + + KisPaintDeviceSP tmpdev1 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev1");; + KisPaintDeviceSP tmpdev2 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");; + KisPaintDeviceSP tmpdev3 = new KisPaintDevice(m_dev->colorSpace(),"transform_tmpdev2");; + KisPaintDeviceSP srcdev = m_dev; + + double xscale = m_xscale; + double yscale = m_yscale; + double xshear = m_xshear; + double yshear = m_yshear; + double rotation = m_rotation; + TQ_INT32 xtranslate = m_xtranslate; + TQ_INT32 ytranslate = m_ytranslate; + + if(rotation < 0.0) + rotation = -fmod(-rotation, 2*M_PI) + 2*M_PI; + else + rotation = fmod(rotation, 2*M_PI); + int rotQuadrant = int(rotation /(M_PI/2) + 0.5) & 3; + + // Figure out how we will do the initial right angle rotations + double tmp; + switch(rotQuadrant) + { + default: // just to shut up the compiler + case 0: + m_progressTotalSteps = 0; + break; + case 1: + rotation -= M_PI/2; + tmp = xscale; + xscale=yscale; + yscale=tmp; + m_progressTotalSteps = r.width() * r.height(); + break; + case 2: + rotation -= M_PI; + m_progressTotalSteps = r.width() * r.height(); + break; + case 3: + rotation -= -M_PI/2 + 2*M_PI; + tmp = xscale; + xscale = yscale; + yscale = tmp; + m_progressTotalSteps = r.width() * r.height(); + break; + } + + // Calculate some auxillary values + yshear = sin(rotation); + xshear = -tan(rotation/2); + xtranslate -= int(xshear*ytranslate); + + // Calculate progress steps + m_progressTotalSteps += int(yscale * r.width() * r.height()); + m_progressTotalSteps += int(xscale * r.width() * (r.height() * yscale + r.width()*yshear)); + + m_lastProgressReport=0; + + // Now that we have everything in place it's time to do the actual right angle rotations + switch(rotQuadrant) + { + default: // just to shut up the compiler + case 0: + break; + case 1: + rotateRight90(srcdev, tmpdev1); + srcdev = tmpdev1; + break; + case 2: + rotate180(srcdev, tmpdev1); + srcdev = tmpdev1; + break; + case 3: + rotateLeft90(srcdev, tmpdev1); + srcdev = tmpdev1; + break; + } + + // Handle simple move case possibly with rotation of 90,180,270 + if(rotation == 0.0 && xscale == 1.0 && yscale == 1.0) + { + if(rotQuadrant==0) + { + // Though not nessesay in the general case because we make several passes + // We need to move (not just copy) the data to a temp dev so we can move them back + rotateNone(srcdev, tmpdev1); + srcdev = tmpdev1; + } + if(m_dev->hasSelection()) + m_dev->selection()->clear(); + + srcdev->move(srcdev->getX() + xtranslate, srcdev->getY() + ytranslate); + rotateNone(srcdev, m_dev); + + //progress info + emit notifyProgressDone(); + m_dev->emitSelectionChanged(); + + return m_cancelRequested; + } + + if ( m_cancelRequested) { + emit notifyProgressDone(); + return false; + } + + transformPass (srcdev, tmpdev2, xscale, yscale*xshear, 0, m_filter, m_fixBorderAlpha); + if(m_dev->hasSelection()) + m_dev->selection()->clear(); + + if ( m_cancelRequested) { + emit notifyProgressDone(); + return false; + } + + // Now do the second pass + transformPass (tmpdev2.data(), tmpdev3.data(), yscale, yshear, ytranslate, m_filter, m_fixBorderAlpha); + + if(m_dev->hasSelection()) + m_dev->selection()->clear(); + + if ( m_cancelRequested) { + emit notifyProgressDone(); + return false; + } + + if (xshear != 0.0) + transformPass (tmpdev3, m_dev, 1.0, xshear, xtranslate, m_filter, m_fixBorderAlpha); + else + { + // No need to filter again when we are only scaling + tmpdev3->move(tmpdev3->getX() + xtranslate, tmpdev3->getY()); + rotateNone(tmpdev3, m_dev); + } + + if (m_dev->parentLayer()) { + m_dev->parentLayer()->setDirty(); + } + //progress info + emit notifyProgressDone(); + m_dev->emitSelectionChanged(); + + return m_cancelRequested; +} diff --git a/chalk/core/kis_vec.cc b/chalk/core/kis_vec.cc deleted file mode 100644 index fa54e1f9..00000000 --- a/chalk/core/kis_vec.cc +++ /dev/null @@ -1,67 +0,0 @@ -/* - * kis_vec.cc - part of KImageShop - * - * Copyright (c) 1999 Matthias Elter - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "kis_vec.h" - -KisVector2D& KisVector2D::normalize() -{ - double length, ilength; - - length = m_x*m_x + m_y*m_y; - length = sqrt (length); - - if (length > epsilon) - { - ilength = 1/length; - m_x *= ilength; - m_y *= ilength; - } - return *this; -} - -KisVector3D& KisVector3D::normalize() -{ - double length, ilength; - - length = m_x*m_x + m_y*m_y + m_z*m_z; - length = sqrt (length); - - if (length > epsilon) - { - ilength = 1/length; - m_x *= ilength; - m_y *= ilength; - m_z *= ilength; - } - return *this; -} - -KisVector3D& KisVector3D::crossProduct(const KisVector3D &v) -{ - double x,y,z; - - x = m_y*v.m_z - m_z*v.m_y; - y = m_z*v.m_x - m_x*v.m_z; - z = m_x*v.m_y - m_y*v.m_x; - m_x=x; m_y=y; m_z=z; - - return *this; -} - diff --git a/chalk/core/kis_vec.cpp b/chalk/core/kis_vec.cpp new file mode 100644 index 00000000..9bd6ada9 --- /dev/null +++ b/chalk/core/kis_vec.cpp @@ -0,0 +1,67 @@ +/* + * kis_vec.cpp - part of KImageShop + * + * Copyright (c) 1999 Matthias Elter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "kis_vec.h" + +KisVector2D& KisVector2D::normalize() +{ + double length, ilength; + + length = m_x*m_x + m_y*m_y; + length = sqrt (length); + + if (length > epsilon) + { + ilength = 1/length; + m_x *= ilength; + m_y *= ilength; + } + return *this; +} + +KisVector3D& KisVector3D::normalize() +{ + double length, ilength; + + length = m_x*m_x + m_y*m_y + m_z*m_z; + length = sqrt (length); + + if (length > epsilon) + { + ilength = 1/length; + m_x *= ilength; + m_y *= ilength; + m_z *= ilength; + } + return *this; +} + +KisVector3D& KisVector3D::crossProduct(const KisVector3D &v) +{ + double x,y,z; + + x = m_y*v.m_z - m_z*v.m_y; + y = m_z*v.m_x - m_x*v.m_z; + z = m_x*v.m_y - m_y*v.m_x; + m_x=x; m_y=y; m_z=z; + + return *this; +} + diff --git a/chalk/core/tests/Makefile.am b/chalk/core/tests/Makefile.am index 97316685..9b78866a 100644 --- a/chalk/core/tests/Makefile.am +++ b/chalk/core/tests/Makefile.am @@ -21,7 +21,7 @@ tdeunittest_kis_image_tester_la_LIBADD = -ltdeunittest ../../libchalkcommon.la tdeunittest_kis_image_tester_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) -tdeunittest_kis_filter_configuration_tester_la_SOURCES = kis_filter_configuration_tester.cc +tdeunittest_kis_filter_configuration_tester_la_SOURCES = kis_filter_configuration_tester.cpp tdeunittest_kis_filter_configuration_tester_la_LIBADD = -ltdeunittest ../../libchalkcommon.la tdeunittest_kis_filter_configuration_tester_la_LDFLAGS = -module $(KDE_CHECK_PLUGIN) $(all_libraries) diff --git a/chalk/core/tests/kis_filter_configuration_tester.cc b/chalk/core/tests/kis_filter_configuration_tester.cc deleted file mode 100644 index 135e7248..00000000 --- a/chalk/core/tests/kis_filter_configuration_tester.cc +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2006 Boudewijn Rempt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include - -#include -#include -#include - -#include "kis_filter_configuration_tester.h" -#include "../kis_filter_configuration.h" - -using namespace KUnitTest; - -TDEUNITTEST_MODULE(tdeunittest_kis_filter_configuration_tester, "KisFilterConfiguration Tester"); -TDEUNITTEST_MODULE_REGISTER_TESTER(KisFilterConfigurationTester); - -void KisFilterConfigurationTester::allTests() -{ - testCreation(); - testSetGetProperty(); - testRoundTrip(); -} - -void KisFilterConfigurationTester::testCreation() -{ - KisFilterConfiguration * kfc = new KisFilterConfiguration("test", 1); - if ( kfc == 0 ) failure("Could not create test filter configuration"); - CHECK(kfc->version(), 1); - CHECK(kfc->name(), TQString("test")); - - delete kfc; - success("testCreation success"); -} - -void KisFilterConfigurationTester::testRoundTrip() -{ - KisFilterConfiguration * kfc = new KisFilterConfiguration("test", 1); - CHECK(kfc->version(), 1); - CHECK(kfc->name(), TQString("test")); - TQString s = kfc->toString(); - delete kfc; - kfc = new KisFilterConfiguration(s); - CHECK(kfc->version(), 1); - CHECK(kfc->name(), TQString("test")); - delete kfc; - success("testDeserializaton success"); -} - -void KisFilterConfigurationTester::testSetGetProperty() -{ -} diff --git a/chalk/core/tests/kis_filter_configuration_tester.cpp b/chalk/core/tests/kis_filter_configuration_tester.cpp new file mode 100644 index 00000000..135e7248 --- /dev/null +++ b/chalk/core/tests/kis_filter_configuration_tester.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2006 Boudewijn Rempt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include + +#include "kis_filter_configuration_tester.h" +#include "../kis_filter_configuration.h" + +using namespace KUnitTest; + +TDEUNITTEST_MODULE(tdeunittest_kis_filter_configuration_tester, "KisFilterConfiguration Tester"); +TDEUNITTEST_MODULE_REGISTER_TESTER(KisFilterConfigurationTester); + +void KisFilterConfigurationTester::allTests() +{ + testCreation(); + testSetGetProperty(); + testRoundTrip(); +} + +void KisFilterConfigurationTester::testCreation() +{ + KisFilterConfiguration * kfc = new KisFilterConfiguration("test", 1); + if ( kfc == 0 ) failure("Could not create test filter configuration"); + CHECK(kfc->version(), 1); + CHECK(kfc->name(), TQString("test")); + + delete kfc; + success("testCreation success"); +} + +void KisFilterConfigurationTester::testRoundTrip() +{ + KisFilterConfiguration * kfc = new KisFilterConfiguration("test", 1); + CHECK(kfc->version(), 1); + CHECK(kfc->name(), TQString("test")); + TQString s = kfc->toString(); + delete kfc; + kfc = new KisFilterConfiguration(s); + CHECK(kfc->version(), 1); + CHECK(kfc->name(), TQString("test")); + delete kfc; + success("testDeserializaton success"); +} + +void KisFilterConfigurationTester::testSetGetProperty() +{ +} diff --git a/chalk/core/tiles/Makefile.am b/chalk/core/tiles/Makefile.am index 7fad1ab9..9ebe93ec 100644 --- a/chalk/core/tiles/Makefile.am +++ b/chalk/core/tiles/Makefile.am @@ -12,9 +12,9 @@ INCLUDES = -I$(srcdir)/../ \ noinst_LTLIBRARIES = libchalktile.la -libchalktile_la_SOURCES = kis_tiledvlineiterator.cc kis_tiledhlineiterator.cc \ - kis_tileddatamanager.cc kis_tile.cc kis_tilediterator.cc kis_tiledrectiterator.cc \ - kis_memento.cc kis_tilemanager.cc kis_tiled_random_accessor.cc +libchalktile_la_SOURCES = kis_tiledvlineiterator.cpp kis_tiledhlineiterator.cpp \ + kis_tileddatamanager.cpp kis_tile.cpp kis_tilediterator.cpp kis_tiledrectiterator.cpp \ + kis_memento.cpp kis_tilemanager.cpp kis_tiled_random_accessor.cpp libchalktile_la_METASOURCES = AUTO diff --git a/chalk/core/tiles/kis_memento.cc b/chalk/core/tiles/kis_memento.cc deleted file mode 100644 index 1181f70a..00000000 --- a/chalk/core/tiles/kis_memento.cc +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2005 Casper Boemann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include "kis_global.h" -#include "kis_memento.h" -#include "kis_tile.h" -#include "kis_tile_global.h" - -KisMemento::KisMemento(TQ_UINT32 pixelSize) : TDEShared() -{ - m_hashTable = new KisTile * [1024]; - TQ_CHECK_PTR(m_hashTable); - - m_redoHashTable = new KisTile * [1024]; - TQ_CHECK_PTR(m_redoHashTable); - - for(int i = 0; i < 1024; i++) - { - m_hashTable [i] = 0; - m_redoHashTable [i] = 0; - } - m_numTiles = 0; - m_defPixel = new TQ_UINT8[pixelSize]; - m_redoDefPixel = new TQ_UINT8[pixelSize]; - m_valid = true; -} - -KisMemento::~KisMemento() -{ - // Deep delete every tile - for(int i = 0; i < 1024; i++) - { - deleteAll(m_hashTable[i]); - deleteAll(m_redoHashTable[i]); - } - delete [] m_hashTable; - delete [] m_redoHashTable; - - // Delete defPixel arrays; - delete [] m_defPixel; - delete [] m_redoDefPixel; -} - -KisMemento::DeletedTileList::~DeletedTileList() -{ - clear(); -} - -void KisMemento::DeletedTileList::clear() -{ - // They are not tiles just references. The actual tiles have already been deleted, - // so just delete the references. - - const DeletedTile *deletedTile = m_firstDeletedTile; - - while (deletedTile) - { - const DeletedTile *d = deletedTile; - deletedTile = deletedTile->next(); - delete d; - } - - m_firstDeletedTile = 0; -} - -void KisMemento::deleteAll(KisTile *tile) -{ - while(tile) - { - KisTile *deltile = tile; - tile = tile->getNext(); - delete deltile; - } -} - -void KisMemento::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const -{ - TQ_INT32 maxX = TQ_INT32_MIN; - TQ_INT32 maxY = TQ_INT32_MIN; - x = TQ_INT32_MAX; - y = TQ_INT32_MAX; - - for(int i = 0; i < 1024; i++) - { - KisTile *tile = m_hashTable[i]; - - while(tile) - { - if(x > tile->getCol() * KisTile::WIDTH) - x = tile->getCol() * KisTile::WIDTH; - if(maxX < (tile->getCol() + 1) * KisTile::WIDTH - 1) - maxX = (tile->getCol() + 1) * KisTile::WIDTH - 1; - if(y > tile->getRow() * KisTile::HEIGHT) - y = tile->getRow() * KisTile::HEIGHT; - if(maxY < (tile->getRow() +1) * KisTile::HEIGHT - 1) - maxY = (tile->getRow() +1) * KisTile::HEIGHT - 1; - - tile = tile->getNext(); - } - } - - if(maxX < x) - w = 0; - else - w = maxX - x +1; - - if(maxY < y) - h = 0; - else - h = maxY - y +1; -} - -TQRect KisMemento::extent() const -{ - TQ_INT32 x; - TQ_INT32 y; - TQ_INT32 w; - TQ_INT32 h; - - extent(x, y, w, h); - - return TQRect(x, y, w, h); -} - -bool KisMemento::containsTile(TQ_INT32 col, TQ_INT32 row, TQ_UINT32 tileHash) const -{ - const KisTile *tile = m_hashTable[tileHash]; - - while (tile != 0) - { - if (tile->getRow() == row && tile->getCol() == col) { - return true; - } - - tile = tile->getNext(); - } - - return false; -} - diff --git a/chalk/core/tiles/kis_memento.cpp b/chalk/core/tiles/kis_memento.cpp new file mode 100644 index 00000000..1181f70a --- /dev/null +++ b/chalk/core/tiles/kis_memento.cpp @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2005 Casper Boemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "kis_global.h" +#include "kis_memento.h" +#include "kis_tile.h" +#include "kis_tile_global.h" + +KisMemento::KisMemento(TQ_UINT32 pixelSize) : TDEShared() +{ + m_hashTable = new KisTile * [1024]; + TQ_CHECK_PTR(m_hashTable); + + m_redoHashTable = new KisTile * [1024]; + TQ_CHECK_PTR(m_redoHashTable); + + for(int i = 0; i < 1024; i++) + { + m_hashTable [i] = 0; + m_redoHashTable [i] = 0; + } + m_numTiles = 0; + m_defPixel = new TQ_UINT8[pixelSize]; + m_redoDefPixel = new TQ_UINT8[pixelSize]; + m_valid = true; +} + +KisMemento::~KisMemento() +{ + // Deep delete every tile + for(int i = 0; i < 1024; i++) + { + deleteAll(m_hashTable[i]); + deleteAll(m_redoHashTable[i]); + } + delete [] m_hashTable; + delete [] m_redoHashTable; + + // Delete defPixel arrays; + delete [] m_defPixel; + delete [] m_redoDefPixel; +} + +KisMemento::DeletedTileList::~DeletedTileList() +{ + clear(); +} + +void KisMemento::DeletedTileList::clear() +{ + // They are not tiles just references. The actual tiles have already been deleted, + // so just delete the references. + + const DeletedTile *deletedTile = m_firstDeletedTile; + + while (deletedTile) + { + const DeletedTile *d = deletedTile; + deletedTile = deletedTile->next(); + delete d; + } + + m_firstDeletedTile = 0; +} + +void KisMemento::deleteAll(KisTile *tile) +{ + while(tile) + { + KisTile *deltile = tile; + tile = tile->getNext(); + delete deltile; + } +} + +void KisMemento::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const +{ + TQ_INT32 maxX = TQ_INT32_MIN; + TQ_INT32 maxY = TQ_INT32_MIN; + x = TQ_INT32_MAX; + y = TQ_INT32_MAX; + + for(int i = 0; i < 1024; i++) + { + KisTile *tile = m_hashTable[i]; + + while(tile) + { + if(x > tile->getCol() * KisTile::WIDTH) + x = tile->getCol() * KisTile::WIDTH; + if(maxX < (tile->getCol() + 1) * KisTile::WIDTH - 1) + maxX = (tile->getCol() + 1) * KisTile::WIDTH - 1; + if(y > tile->getRow() * KisTile::HEIGHT) + y = tile->getRow() * KisTile::HEIGHT; + if(maxY < (tile->getRow() +1) * KisTile::HEIGHT - 1) + maxY = (tile->getRow() +1) * KisTile::HEIGHT - 1; + + tile = tile->getNext(); + } + } + + if(maxX < x) + w = 0; + else + w = maxX - x +1; + + if(maxY < y) + h = 0; + else + h = maxY - y +1; +} + +TQRect KisMemento::extent() const +{ + TQ_INT32 x; + TQ_INT32 y; + TQ_INT32 w; + TQ_INT32 h; + + extent(x, y, w, h); + + return TQRect(x, y, w, h); +} + +bool KisMemento::containsTile(TQ_INT32 col, TQ_INT32 row, TQ_UINT32 tileHash) const +{ + const KisTile *tile = m_hashTable[tileHash]; + + while (tile != 0) + { + if (tile->getRow() == row && tile->getCol() == col) { + return true; + } + + tile = tile->getNext(); + } + + return false; +} + diff --git a/chalk/core/tiles/kis_tile.cc b/chalk/core/tiles/kis_tile.cc deleted file mode 100644 index 743ca82b..00000000 --- a/chalk/core/tiles/kis_tile.cc +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright (c) 2002 Patrick Julien - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -#include -#include - -#include "kis_tile_global.h" -#include "kis_tile.h" -#include "kis_tileddatamanager.h" -#include "kis_tilemanager.h" - -const TQ_INT32 KisTile::WIDTH = 64; -const TQ_INT32 KisTile::HEIGHT = 64; - - -KisTile::KisTile(TQ_INT32 pixelSize, TQ_INT32 col, TQ_INT32 row, const TQ_UINT8 *defPixel) -{ - m_pixelSize = pixelSize; - m_data = 0; - m_nextTile = 0; - m_col = col; - m_row = row; - m_nReadlock = 0; - - allocate(); - - KisTileManager::instance()->registerTile(this); - - setData(defPixel); -} - -KisTile::KisTile(const KisTile& rhs, TQ_INT32 col, TQ_INT32 row) -{ - if (this != &rhs) { - m_pixelSize = rhs.m_pixelSize; - m_data = 0; - m_nextTile = 0; - m_nReadlock = 0; - - allocate(); - - // Assure we have data to copy - rhs.addReader(); - memcpy(m_data, rhs.m_data, WIDTH * HEIGHT * m_pixelSize * sizeof(TQ_UINT8)); - rhs.removeReader(); - - m_col = col; - m_row = row; - - KisTileManager::instance()->registerTile(this); - } -} - -KisTile::KisTile(const KisTile& rhs) -{ - if (this != &rhs) { - m_pixelSize = rhs.m_pixelSize; - m_col = rhs.m_col; - m_row = rhs.m_row; - m_data = 0; - m_nextTile = 0; - m_nReadlock = 0; - - allocate(); - - rhs.addReader(); - memcpy(m_data, rhs.m_data, WIDTH * HEIGHT * m_pixelSize * sizeof(TQ_UINT8)); - rhs.removeReader(); - - KisTileManager::instance()->registerTile(this); - } -} - -KisTile::~KisTile() -{ - KisTileManager::instance()->deregisterTile(this); // goes before the deleting of m_data! - - if (m_data) { -// delete[] m_data; - KisTileManager::instance()->dontNeedTileData(m_data, m_pixelSize); - m_data = 0; - } - assert( !readers() ); -} - -void KisTile::allocate() -{ - if (m_data == 0) { - assert (!readers()); - m_data = KisTileManager::instance()->requestTileData(m_pixelSize); - TQ_CHECK_PTR(m_data); - } -} - -void KisTile::setNext(KisTile *n) -{ - m_nextTile = n; -} - -TQ_UINT8 *KisTile::data(TQ_INT32 x, TQ_INT32 y ) const -{ - addReader(); - removeReader(); - - Q_ASSERT(m_data != 0); - if (m_data == 0) return 0; - - return m_data + m_pixelSize * ( y * WIDTH + x ); -} - -void KisTile::setData(const TQ_UINT8 *pixel) -{ - addReader(); - TQ_UINT8 *dst = m_data; - for(int i=0; i ensureTileLoaded(this); - else if (m_nReadlock < 0) { - kdDebug(41000) << m_nReadlock << endl; - assert(0); - } - assert(m_data); -} - -void KisTile::removeReader() const -{ - if (--m_nReadlock == 0) - KisTileManager::instance()->maySwapTile(this); -} diff --git a/chalk/core/tiles/kis_tile.cpp b/chalk/core/tiles/kis_tile.cpp new file mode 100644 index 00000000..743ca82b --- /dev/null +++ b/chalk/core/tiles/kis_tile.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2002 Patrick Julien + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include +#include + +#include "kis_tile_global.h" +#include "kis_tile.h" +#include "kis_tileddatamanager.h" +#include "kis_tilemanager.h" + +const TQ_INT32 KisTile::WIDTH = 64; +const TQ_INT32 KisTile::HEIGHT = 64; + + +KisTile::KisTile(TQ_INT32 pixelSize, TQ_INT32 col, TQ_INT32 row, const TQ_UINT8 *defPixel) +{ + m_pixelSize = pixelSize; + m_data = 0; + m_nextTile = 0; + m_col = col; + m_row = row; + m_nReadlock = 0; + + allocate(); + + KisTileManager::instance()->registerTile(this); + + setData(defPixel); +} + +KisTile::KisTile(const KisTile& rhs, TQ_INT32 col, TQ_INT32 row) +{ + if (this != &rhs) { + m_pixelSize = rhs.m_pixelSize; + m_data = 0; + m_nextTile = 0; + m_nReadlock = 0; + + allocate(); + + // Assure we have data to copy + rhs.addReader(); + memcpy(m_data, rhs.m_data, WIDTH * HEIGHT * m_pixelSize * sizeof(TQ_UINT8)); + rhs.removeReader(); + + m_col = col; + m_row = row; + + KisTileManager::instance()->registerTile(this); + } +} + +KisTile::KisTile(const KisTile& rhs) +{ + if (this != &rhs) { + m_pixelSize = rhs.m_pixelSize; + m_col = rhs.m_col; + m_row = rhs.m_row; + m_data = 0; + m_nextTile = 0; + m_nReadlock = 0; + + allocate(); + + rhs.addReader(); + memcpy(m_data, rhs.m_data, WIDTH * HEIGHT * m_pixelSize * sizeof(TQ_UINT8)); + rhs.removeReader(); + + KisTileManager::instance()->registerTile(this); + } +} + +KisTile::~KisTile() +{ + KisTileManager::instance()->deregisterTile(this); // goes before the deleting of m_data! + + if (m_data) { +// delete[] m_data; + KisTileManager::instance()->dontNeedTileData(m_data, m_pixelSize); + m_data = 0; + } + assert( !readers() ); +} + +void KisTile::allocate() +{ + if (m_data == 0) { + assert (!readers()); + m_data = KisTileManager::instance()->requestTileData(m_pixelSize); + TQ_CHECK_PTR(m_data); + } +} + +void KisTile::setNext(KisTile *n) +{ + m_nextTile = n; +} + +TQ_UINT8 *KisTile::data(TQ_INT32 x, TQ_INT32 y ) const +{ + addReader(); + removeReader(); + + Q_ASSERT(m_data != 0); + if (m_data == 0) return 0; + + return m_data + m_pixelSize * ( y * WIDTH + x ); +} + +void KisTile::setData(const TQ_UINT8 *pixel) +{ + addReader(); + TQ_UINT8 *dst = m_data; + for(int i=0; i ensureTileLoaded(this); + else if (m_nReadlock < 0) { + kdDebug(41000) << m_nReadlock << endl; + assert(0); + } + assert(m_data); +} + +void KisTile::removeReader() const +{ + if (--m_nReadlock == 0) + KisTileManager::instance()->maySwapTile(this); +} diff --git a/chalk/core/tiles/kis_tiled_random_accessor.cc b/chalk/core/tiles/kis_tiled_random_accessor.cc deleted file mode 100644 index 159faf18..00000000 --- a/chalk/core/tiles/kis_tiled_random_accessor.cc +++ /dev/null @@ -1,115 +0,0 @@ -/* - * copyright (c) 2006 Cyrille Berger - * - * this program is free software; you can redistribute it and/or modify - * it under the terms of the gnu general public license as published by - * the free software foundation; either version 2 of the license, or - * (at your option) any later version. - * - * this program is distributed in the hope that it will be useful, - * but without any warranty; without even the implied warranty of - * merchantability or fitness for a particular purpose. see the - * gnu general public license for more details. - * - * you should have received a copy of the gnu general public license - * along with this program; if not, write to the free software - * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. - */ -#include "kis_tiled_random_accessor.h" - - -const TQ_UINT32 KisTiledRandomAccessor::CACHESIZE = 4; // Define the number of tiles we keep in cache - -KisTiledRandomAccessor::KisTiledRandomAccessor(KisTiledDataManager *ktm, TQ_INT32 x, TQ_INT32 y, bool writable) : m_ktm(ktm), m_tilesCache(new KisTileInfo*[4]), m_tilesCacheSize(0), m_pixelSize (m_ktm->pixelSize()), m_writable(writable) -{ - Q_ASSERT(ktm != 0); - moveTo(x, y); -} - -KisTiledRandomAccessor::~KisTiledRandomAccessor() -{ - for( uint i = 0; i < m_tilesCacheSize; i++) - { - m_tilesCache[i]->tile->removeReader(); - m_tilesCache[i]->oldtile->removeReader(); - delete m_tilesCache[i]; - } - delete m_tilesCache; -} - -void KisTiledRandomAccessor::moveTo(TQ_INT32 x, TQ_INT32 y) -{ - // Look in the cache if the tile if the data is available - for( uint i = 0; i < m_tilesCacheSize; i++) - { - if( x >= m_tilesCache[i]->area_x1 && x <= m_tilesCache[i]->area_x2 && - y >= m_tilesCache[i]->area_y1 && y <= m_tilesCache[i]->area_y2 ) - { - KisTileInfo* kti = m_tilesCache[i]; - TQ_UINT32 offset = x - kti->area_x1 + (y -kti->area_y1) * KisTile::WIDTH; - offset *= m_pixelSize; - m_data = kti->data + offset; - m_oldData = kti->oldData + offset; - if(i > 0) - { - memmove(m_tilesCache+1,m_tilesCache, i * sizeof(KisTileInfo*)); - m_tilesCache[0] = kti; - } - return; - } - } - // The tile wasn't in cache - if(m_tilesCacheSize == KisTiledRandomAccessor::CACHESIZE ) - { // Remove last element of cache - m_tilesCache[CACHESIZE-1]->tile->removeReader(); - m_tilesCache[CACHESIZE-1]->oldtile->removeReader(); - delete m_tilesCache[CACHESIZE-1]; - } else { - m_tilesCacheSize++; - } - TQ_UINT32 col = xToCol( x ); - TQ_UINT32 row = yToRow( y ); - KisTileInfo* kti = fetchTileData(col, row); - TQ_UINT32 offset = x - kti->area_x1 + (y - kti->area_y1) * KisTile::WIDTH; - offset *= m_pixelSize; - m_data = kti->data + offset; - m_oldData = kti->oldData + offset; - memmove(m_tilesCache+1,m_tilesCache, (KisTiledRandomAccessor::CACHESIZE-1) * sizeof(KisTileInfo*)); - m_tilesCache[0] = kti; -} - - -TQ_UINT8 * KisTiledRandomAccessor::rawData() const -{ - return m_data; -} - - -const TQ_UINT8 * KisTiledRandomAccessor::oldRawData() const -{ -#ifdef DEBUG - kdWarning(!m_ktm->hasCurrentMemento(), DBG_AREA_TILES) << "Accessing oldRawData() when no transaction is in progress.\n"; -#endif - return m_oldData; -} - -KisTiledRandomAccessor::KisTileInfo* KisTiledRandomAccessor::fetchTileData(TQ_INT32 col, TQ_INT32 row) -{ - KisTileInfo* kti = new KisTileInfo; - kti->tile = m_ktm->getTile(col, row, m_writable); - - kti->tile->addReader(); - - kti->data = kti->tile->data(); - - kti->area_x1 = col * KisTile::HEIGHT; - kti->area_y1 = row * KisTile::WIDTH; - kti->area_x2 = kti->area_x1 + KisTile::HEIGHT - 2; - kti->area_y2 = kti->area_y1 + KisTile::WIDTH - 2; - - // set old data - kti->oldtile = m_ktm->getOldTile(col, row, kti->tile); - kti->oldtile->addReader(); - kti->oldData = kti->oldtile->data(); - return kti; -} diff --git a/chalk/core/tiles/kis_tiled_random_accessor.cpp b/chalk/core/tiles/kis_tiled_random_accessor.cpp new file mode 100644 index 00000000..159faf18 --- /dev/null +++ b/chalk/core/tiles/kis_tiled_random_accessor.cpp @@ -0,0 +1,115 @@ +/* + * copyright (c) 2006 Cyrille Berger + * + * this program is free software; you can redistribute it and/or modify + * it under the terms of the gnu general public license as published by + * the free software foundation; either version 2 of the license, or + * (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 675 mass ave, cambridge, ma 02139, usa. + */ +#include "kis_tiled_random_accessor.h" + + +const TQ_UINT32 KisTiledRandomAccessor::CACHESIZE = 4; // Define the number of tiles we keep in cache + +KisTiledRandomAccessor::KisTiledRandomAccessor(KisTiledDataManager *ktm, TQ_INT32 x, TQ_INT32 y, bool writable) : m_ktm(ktm), m_tilesCache(new KisTileInfo*[4]), m_tilesCacheSize(0), m_pixelSize (m_ktm->pixelSize()), m_writable(writable) +{ + Q_ASSERT(ktm != 0); + moveTo(x, y); +} + +KisTiledRandomAccessor::~KisTiledRandomAccessor() +{ + for( uint i = 0; i < m_tilesCacheSize; i++) + { + m_tilesCache[i]->tile->removeReader(); + m_tilesCache[i]->oldtile->removeReader(); + delete m_tilesCache[i]; + } + delete m_tilesCache; +} + +void KisTiledRandomAccessor::moveTo(TQ_INT32 x, TQ_INT32 y) +{ + // Look in the cache if the tile if the data is available + for( uint i = 0; i < m_tilesCacheSize; i++) + { + if( x >= m_tilesCache[i]->area_x1 && x <= m_tilesCache[i]->area_x2 && + y >= m_tilesCache[i]->area_y1 && y <= m_tilesCache[i]->area_y2 ) + { + KisTileInfo* kti = m_tilesCache[i]; + TQ_UINT32 offset = x - kti->area_x1 + (y -kti->area_y1) * KisTile::WIDTH; + offset *= m_pixelSize; + m_data = kti->data + offset; + m_oldData = kti->oldData + offset; + if(i > 0) + { + memmove(m_tilesCache+1,m_tilesCache, i * sizeof(KisTileInfo*)); + m_tilesCache[0] = kti; + } + return; + } + } + // The tile wasn't in cache + if(m_tilesCacheSize == KisTiledRandomAccessor::CACHESIZE ) + { // Remove last element of cache + m_tilesCache[CACHESIZE-1]->tile->removeReader(); + m_tilesCache[CACHESIZE-1]->oldtile->removeReader(); + delete m_tilesCache[CACHESIZE-1]; + } else { + m_tilesCacheSize++; + } + TQ_UINT32 col = xToCol( x ); + TQ_UINT32 row = yToRow( y ); + KisTileInfo* kti = fetchTileData(col, row); + TQ_UINT32 offset = x - kti->area_x1 + (y - kti->area_y1) * KisTile::WIDTH; + offset *= m_pixelSize; + m_data = kti->data + offset; + m_oldData = kti->oldData + offset; + memmove(m_tilesCache+1,m_tilesCache, (KisTiledRandomAccessor::CACHESIZE-1) * sizeof(KisTileInfo*)); + m_tilesCache[0] = kti; +} + + +TQ_UINT8 * KisTiledRandomAccessor::rawData() const +{ + return m_data; +} + + +const TQ_UINT8 * KisTiledRandomAccessor::oldRawData() const +{ +#ifdef DEBUG + kdWarning(!m_ktm->hasCurrentMemento(), DBG_AREA_TILES) << "Accessing oldRawData() when no transaction is in progress.\n"; +#endif + return m_oldData; +} + +KisTiledRandomAccessor::KisTileInfo* KisTiledRandomAccessor::fetchTileData(TQ_INT32 col, TQ_INT32 row) +{ + KisTileInfo* kti = new KisTileInfo; + kti->tile = m_ktm->getTile(col, row, m_writable); + + kti->tile->addReader(); + + kti->data = kti->tile->data(); + + kti->area_x1 = col * KisTile::HEIGHT; + kti->area_y1 = row * KisTile::WIDTH; + kti->area_x2 = kti->area_x1 + KisTile::HEIGHT - 2; + kti->area_y2 = kti->area_y1 + KisTile::WIDTH - 2; + + // set old data + kti->oldtile = m_ktm->getOldTile(col, row, kti->tile); + kti->oldtile->addReader(); + kti->oldData = kti->oldtile->data(); + return kti; +} diff --git a/chalk/core/tiles/kis_tileddatamanager.cc b/chalk/core/tiles/kis_tileddatamanager.cc deleted file mode 100644 index 4586903c..00000000 --- a/chalk/core/tiles/kis_tileddatamanager.cc +++ /dev/null @@ -1,1044 +0,0 @@ -/* - * Copyright (c) 2004 Casper Boemann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include - -#include - -#include - -#include "kis_global.h" -#include "kis_debug_areas.h" -#include "kis_tileddatamanager.h" -#include "kis_tilediterator.h" -#include "kis_tile.h" -#include "kis_memento.h" -#include "kis_tilemanager.h" - -/* The data area is divided into tiles each say 64x64 pixels (defined at compiletime) - * The tiles are laid out in a matrix that can have negative indexes. - * The matrix grows automatically if needed (a call for writeacces to a tile outside the current extent) - * Even though the matrix has grown it may still not contain tiles at specific positions. They are created on demand - */ - -KisTiledDataManager::KisTiledDataManager(TQ_UINT32 pixelSize, const TQ_UINT8 *defPixel) -{ - m_pixelSize = pixelSize; - - m_defPixel = new TQ_UINT8[m_pixelSize]; - TQ_CHECK_PTR(m_defPixel); - memcpy(m_defPixel, defPixel, m_pixelSize); - - m_defaultTile = new KisTile(pixelSize,0,0, m_defPixel); - TQ_CHECK_PTR(m_defaultTile); - - m_hashTable = new KisTile * [1024]; - TQ_CHECK_PTR(m_hashTable); - - for(int i = 0; i < 1024; i++) - m_hashTable [i] = 0; - m_numTiles = 0; - m_currentMemento = 0; - m_extentMinX = TQ_INT32_MAX; - m_extentMinY = TQ_INT32_MAX; - m_extentMaxX = TQ_INT32_MIN; - m_extentMaxY = TQ_INT32_MIN; -} - -KisTiledDataManager::KisTiledDataManager(const KisTiledDataManager & dm) - : TDEShared() -{ - m_pixelSize = dm.m_pixelSize; - - m_defPixel = new TQ_UINT8[m_pixelSize]; - TQ_CHECK_PTR(m_defPixel); - memcpy(m_defPixel, dm.m_defPixel, m_pixelSize); - - m_defaultTile = new KisTile(*dm.m_defaultTile, dm.m_defaultTile->getCol(), dm.m_defaultTile->getRow()); - TQ_CHECK_PTR(m_defaultTile); - - m_hashTable = new KisTile * [1024]; - TQ_CHECK_PTR(m_hashTable); - - m_numTiles = 0; - m_currentMemento = 0; - m_extentMinX = dm.m_extentMinX; - m_extentMinY = dm.m_extentMinY; - m_extentMaxX = dm.m_extentMaxX; - m_extentMaxY = dm.m_extentMaxY; - - // Deep copy every tile. XXX: Make this copy-on-write! - for(int i = 0; i < 1024; i++) - { - const KisTile *tile = dm.m_hashTable[i]; - - m_hashTable[i] = 0; - - while(tile) - { - KisTile *newtile = new KisTile(*tile, tile->getCol(), tile->getRow()); - TQ_CHECK_PTR(newtile); - - newtile->setNext(m_hashTable[i]); - m_hashTable[i] = newtile; - tile = tile->getNext(); - - m_numTiles++; - } - } - -} - -KisTiledDataManager::~KisTiledDataManager() -{ - // Deep delete every tile - for(int i = 0; i < 1024; i++) - { - const KisTile *tile = m_hashTable[i]; - - while(tile) - { - const KisTile *deltile = tile; - tile = tile->getNext(); - delete deltile; - } - } - delete [] m_hashTable; - delete m_defaultTile; - delete [] m_defPixel; -} - -void KisTiledDataManager::setDefaultPixel(const TQ_UINT8 *defPixel) -{ - if (defPixel == 0) return; - - memcpy(m_defPixel, defPixel, m_pixelSize); - - m_defaultTile->setData(m_defPixel); -} - -bool KisTiledDataManager::write(KoStore *store) -{ - - if (store == 0) return false; - //Q_ASSERT(store != 0); - - char str[80]; - - sprintf(str, "%d\n", m_numTiles); - store->write(str,strlen(str)); - - for(int i = 0; i < 1024; i++) - { - const KisTile *tile = m_hashTable[i]; - - while(tile) - { - sprintf(str, "%d,%d,%d,%d\n", tile->getCol() * KisTile::WIDTH, - tile->getRow() * KisTile::HEIGHT, - KisTile::WIDTH, KisTile::HEIGHT); - store->write(str,strlen(str)); - - tile->addReader(); - store->write((char *)tile->m_data, KisTile::HEIGHT * KisTile::WIDTH * m_pixelSize); - tile->removeReader(); - - tile = tile->getNext(); - } - } - - return true; -} -bool KisTiledDataManager::read(KoStore *store) -{ - if (store == 0) return false; - //Q_ASSERT(store != 0); - - char str[80]; - TQ_INT32 x,y,w,h; - - TQIODevice *stream = store->device(); - if (stream == 0) return false; - //Q_ASSERT(stream != 0); - - stream->readLine(str, 79); - - sscanf(str,"%u",&m_numTiles); - - for(TQ_UINT32 i = 0; i < m_numTiles; i++) - { - stream->readLine(str, 79); - sscanf(str,"%d,%d,%d,%d",&x,&y,&w,&h); - - // the following is only correct as long as tile size is not changed - // The first time we change tilesize the dimensions just read needs to be respected - // but for now we just assume that tiles are the same size as ever. - TQ_INT32 row = yToRow(y); - TQ_INT32 col = xToCol(x); - TQ_UINT32 tileHash = calcTileHash(col, row); - - KisTile *tile = new KisTile(m_pixelSize, col, row, m_defPixel); - TQ_CHECK_PTR(tile); - - updateExtent(col,row); - - tile->addReader(); - store->read((char *)tile->m_data, KisTile::HEIGHT * KisTile::WIDTH * m_pixelSize); - tile->removeReader(); - - tile->setNext(m_hashTable[tileHash]); - m_hashTable[tileHash] = tile; - } - return true; -} - -void KisTiledDataManager::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const -{ - x = m_extentMinX; - y = m_extentMinY; - - if (m_extentMaxX >= m_extentMinX) { - w = m_extentMaxX - m_extentMinX + 1; - } else { - w = 0; - } - - if (m_extentMaxY >= m_extentMinY) { - h = m_extentMaxY - m_extentMinY + 1; - } else { - h = 0; - } -} - -TQRect KisTiledDataManager::extent() const -{ - TQ_INT32 x; - TQ_INT32 y; - TQ_INT32 w; - TQ_INT32 h; - - extent(x, y, w, h); - - return TQRect(x, y, w, h); -} - -void KisTiledDataManager::setExtent(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) -{ - TQRect newRect = TQRect(x, y, w, h).normalize(); - //printRect("newRect", newRect); - TQRect oldRect = TQRect(m_extentMinX, m_extentMinY, m_extentMaxX - m_extentMinX + 1, m_extentMaxY - m_extentMinY + 1).normalize(); - //printRect("oldRect", oldRect); - - // Do nothing if the desired size is bigger than we currently are: that is handled by the autoextending automatically - if (newRect.contains(oldRect)) return; - - // Loop through all tiles, if a tile is wholly outside the extent, add to the memento, then delete it, - // if the tile is partially outside the extent, clear the outside pixels to the default pixel. - for(int tileHash = 0; tileHash < 1024; tileHash++) - { - KisTile *tile = m_hashTable[tileHash]; - KisTile *previousTile = 0; - - while(tile) - { - TQRect tileRect = TQRect(tile->getCol() * KisTile::WIDTH, tile->getRow() * KisTile::HEIGHT, KisTile::WIDTH, KisTile::HEIGHT); - //printRect("tileRect", tileRect); - - if (newRect.contains(tileRect)) { - // Completely inside, do nothing - previousTile = tile; - tile = tile->getNext(); - } - else { - ensureTileMementoed(tile->getCol(), tile->getRow(), tileHash, tile); - - if (newRect.intersects(tileRect)) { - - // Create the intersection of the tile and new rect - TQRect intersection = newRect.intersect(tileRect); - //printRect("intersection", intersection); - intersection.setRect(intersection.x() - tileRect.x(), intersection.y() - tileRect.y(), intersection.width(), intersection.height()); - - // This can be done a lot more efficiently, no doubt, by clearing runs of pixels to the left and the right of - // the intersecting line. - tile->addReader(); - for (int y = 0; y < KisTile::HEIGHT; ++y) { - for (int x = 0; x < KisTile::WIDTH; ++x) { - if (!intersection.contains(x,y)) { - TQ_UINT8 * ptr = tile->data(x, y); - memcpy(ptr, m_defPixel, m_pixelSize); - } - } - } - tile->removeReader(); - previousTile = tile; - tile = tile->getNext(); - } - else { - KisTile *deltile = tile; - tile = tile->getNext(); - - m_numTiles--; - - if (previousTile) - previousTile->setNext(tile); - else - m_hashTable[tileHash] = tile; - delete deltile; - } - } - } - } - - // Set the extent correctly - m_extentMinX = x; - m_extentMinY = y; - m_extentMaxX = x + w - 1; - m_extentMaxY = y + h - 1; -} - -void KisTiledDataManager::recalculateExtent() -{ - m_extentMinX = TQ_INT32_MAX; - m_extentMinY = TQ_INT32_MAX; - m_extentMaxX = TQ_INT32_MIN; - m_extentMaxY = TQ_INT32_MIN; - - // Loop through all tiles. - for (int tileHash = 0; tileHash < 1024; tileHash++) - { - const KisTile *tile = m_hashTable[tileHash]; - - while (tile) - { - updateExtent(tile->getCol(), tile->getRow()); - tile = tile->getNext(); - } - } -} - -void KisTiledDataManager::clear(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, TQ_UINT8 clearValue) -{ - if (w < 1 || h < 1) { - return; - } - - TQ_INT32 firstColumn = xToCol(x); - TQ_INT32 lastColumn = xToCol(x + w - 1); - - TQ_INT32 firstRow = yToRow(y); - TQ_INT32 lastRow = yToRow(y + h - 1); - - TQRect clearRect(x, y, w, h); - - const TQ_UINT32 rowStride = KisTile::WIDTH * m_pixelSize; - - for (TQ_INT32 row = firstRow; row <= lastRow; ++row) { - for (TQ_INT32 column = firstColumn; column <= lastColumn; ++column) { - - KisTile *tile = getTile(column, row, true); - TQRect tileRect = tile->extent(); - - TQRect clearTileRect = clearRect & tileRect; - - tile->addReader(); - if (clearTileRect == tileRect) { - // Clear whole tile - memset(tile->data(), clearValue, KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize); - } else { - - TQ_UINT32 rowsRemaining = clearTileRect.height(); - TQ_UINT8 *dst = tile->data(clearTileRect.x() - tileRect.x(), clearTileRect.y() - tileRect.y()); - - while (rowsRemaining > 0) { - memset(dst, clearValue, clearTileRect.width() * m_pixelSize); - dst += rowStride; - --rowsRemaining; - } - } - tile->removeReader(); - } - } -} - -void KisTiledDataManager::clear(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const TQ_UINT8 *clearPixel) -{ - Q_ASSERT(clearPixel != 0); - - if (clearPixel == 0 || w < 1 || h < 1) { - return; - } - - bool pixelBytesAreTheSame = true; - - for (TQ_UINT32 i = 0; i < m_pixelSize; ++i) { - if (clearPixel[i] != clearPixel[0]) { - pixelBytesAreTheSame = false; - break; - } - } - - if (pixelBytesAreTheSame) { - clear(x, y, w, h, clearPixel[0]); - } else { - - TQ_INT32 firstColumn = xToCol(x); - TQ_INT32 lastColumn = xToCol(x + w - 1); - - TQ_INT32 firstRow = yToRow(y); - TQ_INT32 lastRow = yToRow(y + h - 1); - - TQRect clearRect(x, y, w, h); - - const TQ_UINT32 rowStride = KisTile::WIDTH * m_pixelSize; - - TQ_UINT8 *clearPixelData = 0; - - if (w >= KisTile::WIDTH && h >= KisTile::HEIGHT) { - - // There might be a whole tile to be cleared so generate a cleared tile. - clearPixelData = new TQ_UINT8[KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize]; - - TQ_UINT8 *dst = clearPixelData; - TQ_UINT32 pixelsRemaining = KisTile::WIDTH; - - // Generate one row - while (pixelsRemaining > 0) { - memcpy(dst, clearPixel, m_pixelSize); - dst += m_pixelSize; - --pixelsRemaining; - } - - TQ_UINT32 rowsRemaining = KisTile::HEIGHT - 1; - - // Copy to the rest of the rows. - while (rowsRemaining > 0) { - memcpy(dst, clearPixelData, rowStride); - dst += rowStride; - --rowsRemaining; - } - - } else { - - // Generate one row - TQ_UINT32 maxRunLength = TQMIN(w, KisTile::WIDTH); - - clearPixelData = new TQ_UINT8[maxRunLength * m_pixelSize]; - - TQ_UINT8 *dst = clearPixelData; - TQ_UINT32 pixelsRemaining = maxRunLength; - - while (pixelsRemaining > 0) { - memcpy(dst, clearPixel, m_pixelSize); - dst += m_pixelSize; - --pixelsRemaining; - } - } - - for (TQ_INT32 row = firstRow; row <= lastRow; ++row) { - for (TQ_INT32 column = firstColumn; column <= lastColumn; ++column) { - - KisTile *tile = getTile(column, row, true); - TQRect tileRect = tile->extent(); - - TQRect clearTileRect = clearRect & tileRect; - - if (clearTileRect == tileRect) { - - // Clear whole tile - tile->addReader(); - memcpy(tile->data(), clearPixelData, KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize); - tile->removeReader(); - } else { - - TQ_UINT32 rowsRemaining = clearTileRect.height(); - tile->addReader(); - TQ_UINT8 *dst = tile->data(clearTileRect.x() - tileRect.x(), clearTileRect.y() - tileRect.y()); - - while (rowsRemaining > 0) { - memcpy(dst, clearPixelData, clearTileRect.width() * m_pixelSize); - dst += rowStride; - --rowsRemaining; - } - tile->removeReader(); - } - } - } - - delete [] clearPixelData; - } -} - -void KisTiledDataManager::clear() -{ - // Loop through all tiles, add to the memento, then delete it, - for(int tileHash = 0; tileHash < 1024; tileHash++) - { - const KisTile *tile = m_hashTable[tileHash]; - - while(tile) - { - ensureTileMementoed(tile->getCol(), tile->getRow(), tileHash, tile); - - const KisTile *deltile = tile; - tile = tile->getNext(); - - delete deltile; - } - m_hashTable[tileHash] = 0; - } - - m_numTiles = 0; - - // Set the extent correctly - m_extentMinX = TQ_INT32_MAX; - m_extentMinY = TQ_INT32_MAX; - m_extentMaxX = TQ_INT32_MIN; - m_extentMaxY = TQ_INT32_MIN; -} - -void KisTiledDataManager::paste(KisDataManagerSP data, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 dx, TQ_INT32 dy, - TQ_INT32 w, TQ_INT32 h) -{ - //CBR_MISSING - sx=sy=dx=dy=w=h;data=0; -} - - -TQ_UINT32 KisTiledDataManager::calcTileHash(TQ_INT32 col, TQ_INT32 row) -{ - return ((row << 5) + (col & 0x1F)) & 0x3FF; -} - -KisMementoSP KisTiledDataManager::getMemento() -{ - m_currentMemento = new KisMemento(m_pixelSize); - TQ_CHECK_PTR(m_currentMemento); - - memcpy(m_currentMemento->m_defPixel, m_defPixel, m_pixelSize); - - return m_currentMemento; -} - -void KisTiledDataManager::rollback(KisMementoSP memento) -{ - if (memento == 0) return; - //Q_ASSERT(memento != 0); - - if (m_currentMemento != 0) { - // Undo means our current memento is no longer valid so remove it. - m_currentMemento = 0; - } - - // Rollback means restoring all of the tiles in the memento to our hashtable. - - // But first clear the memento redo hashtable. - // This is nessesary as new changes might have been done since last rollback (automatic filters) - for(int i = 0; i < 1024; i++) - { - memento->deleteAll(memento->m_redoHashTable[i]); - memento->m_redoHashTable[i]=0; - } - - // Also clear the table of deleted tiles - memento->clearTilesToDeleteOnRedo(); - - // Now on to the real rollback - - memcpy(memento->m_redoDefPixel, m_defPixel, m_pixelSize); - setDefaultPixel(memento->m_defPixel); - - for(int i = 0; i < 1024; i++) - { - KisTile *tile = memento->m_hashTable[i]; - - while(tile) - { - // The memento has a tile stored that we need to roll back - // Now find the corresponding one in our hashtable - KisTile *curTile = m_hashTable[i]; - KisTile *preTile = 0; - while(curTile) - { - if(curTile->getRow() == tile->getRow() && curTile->getCol() == tile->getCol()) - { - break; - } - preTile = curTile; - curTile = curTile->getNext(); - } - - if(curTile) - { - // Remove it from our hashtable - if(preTile) - preTile->setNext(curTile->getNext()); - else - m_hashTable[i]= curTile->getNext(); - - m_numTiles--; - - // And put it in the redo hashtable of the memento - curTile->setNext(memento->m_redoHashTable[i]); - memento->m_redoHashTable[i] = curTile; - } - else - { - memento->addTileToDeleteOnRedo(tile->getCol(), tile->getRow()); - // As we are pratically adding a new tile we need to update the extent - updateExtent(tile->getCol(), tile->getRow()); - } - - // Put a copy of the memento tile into our hashtable - curTile = new KisTile(*tile); - TQ_CHECK_PTR(curTile); - m_numTiles++; - - curTile->setNext(m_hashTable[i]); - m_hashTable[i] = curTile; - - tile = tile->getNext(); - } - } - - if (memento->tileListToDeleteOnUndo() != 0) { - // XXX: We currently add these tiles above, only to delete them again here. - deleteTiles(memento->tileListToDeleteOnUndo()); - } -} - -void KisTiledDataManager::rollforward(KisMementoSP memento) -{ - if (memento == 0) return; - //Q_ASSERT(memento != 0); - - if (m_currentMemento != 0) { - // Redo means our current memento is no longer valid so remove it. - m_currentMemento = 0; - } - - // Rollforward means restoring all of the tiles in the memento's redo to our hashtable. - - setDefaultPixel(memento->m_redoDefPixel); - - for(int i = 0; i < 1024; i++) - { - KisTile *tile = memento->m_redoHashTable[i]; - - while(tile) - { - // The memento has a tile stored that we need to roll forward - // Now find the corresponding one in our hashtable - KisTile *curTile = m_hashTable[i]; - KisTile *preTile = 0; - while(curTile) - { - if(curTile->getRow() == tile->getRow() && curTile->getCol() == tile->getCol()) - { - break; - } - preTile = curTile; - curTile = curTile->getNext(); - } - - if (curTile) - { - // Remove it from our hashtable - if(preTile) - preTile->setNext(curTile->getNext()); - else - m_hashTable[i]= curTile->getNext(); - - // And delete it (it's equal to the one stored in the memento's undo) - m_numTiles--; - delete curTile; - } - - // Put a copy of the memento tile into our hashtable - curTile = new KisTile(*tile); - TQ_CHECK_PTR(curTile); - - curTile->setNext(m_hashTable[i]); - m_hashTable[i] = curTile; - m_numTiles++; - updateExtent(curTile->getCol(), curTile->getRow()); - - tile = tile->getNext(); - } - } - - // Roll forward also means re-deleting the tiles that was deleted but restored by the undo - if (memento->tileListToDeleteOnRedo() != 0) { - deleteTiles(memento->tileListToDeleteOnRedo()); - } -} - -void KisTiledDataManager::deleteTiles(const KisMemento::DeletedTile *d) -{ - while (d) - { - TQ_UINT32 tileHash = calcTileHash(d->col(), d->row()); - KisTile *curTile = m_hashTable[tileHash]; - KisTile *preTile = 0; - while(curTile) - { - if(curTile->getRow() == d->row() && curTile->getCol() == d->col()) - { - break; - } - preTile = curTile; - curTile = curTile->getNext(); - } - if (curTile) { - // Remove it from our hashtable - if(preTile) - preTile->setNext(curTile->getNext()); - else - m_hashTable[tileHash] = curTile->getNext(); - - // And delete it (it's equal to the one stored in the memento's undo) - m_numTiles--; - delete curTile; - } - d = d->next(); - } - - recalculateExtent(); -} - -void KisTiledDataManager::ensureTileMementoed(TQ_INT32 col, TQ_INT32 row, TQ_UINT32 tileHash, const KisTile *refTile) -{ - if (refTile == 0) return; - //Q_ASSERT(refTile != 0); - - // Basically we search for the tile in the current memento, and if it's already there we do nothing, otherwise - // we make a copy of the tile and put it in the current memento - - if(!m_currentMemento) - return; - - KisTile *tile = m_currentMemento->m_hashTable[tileHash]; - while(tile != 0) - { - if(tile->getRow() == row && tile->getCol() == col) - break; - - tile = tile->getNext(); - } - if(tile) - return; // it has allready been stored - - tile = new KisTile(*refTile); - TQ_CHECK_PTR(tile); - - tile->setNext(m_currentMemento->m_hashTable[tileHash]); - m_currentMemento->m_hashTable[tileHash] = tile; - m_currentMemento->m_numTiles++; -} - -void KisTiledDataManager::updateExtent(TQ_INT32 col, TQ_INT32 row) -{ - if(m_extentMinX > col * KisTile::WIDTH) - m_extentMinX = col * KisTile::WIDTH; - if(m_extentMaxX < (col+1) * KisTile::WIDTH - 1) - m_extentMaxX = (col+1) * KisTile::WIDTH - 1; - if(m_extentMinY > row * KisTile::HEIGHT) - m_extentMinY = row * KisTile::HEIGHT; - if(m_extentMaxY < (row+1) * KisTile::HEIGHT - 1) - m_extentMaxY = (row+1) * KisTile::HEIGHT - 1; -} - -KisTile *KisTiledDataManager::getTile(TQ_INT32 col, TQ_INT32 row, bool writeAccess) -{ - TQ_UINT32 tileHash = calcTileHash(col, row); - - // Lookup tile in hash table - KisTile *tile = m_hashTable[tileHash]; - while(tile != 0) - { - if(tile->getRow() == row && tile->getCol() == col) - break; - - tile = tile->getNext(); - } - - // Might not have been created yet - if(!tile) - { - if(writeAccess) - { - // Create a new tile - tile = new KisTile(*m_defaultTile, col, row); - TQ_CHECK_PTR(tile); - - tile->setNext(m_hashTable[tileHash]); - m_hashTable[tileHash] = tile; - m_numTiles++; - updateExtent(col, row); - - if (m_currentMemento && !m_currentMemento->containsTile(col, row, tileHash)) { - m_currentMemento->addTileToDeleteOnUndo(col, row); - } - } - else - // If only read access then it's enough to share a default tile - tile = m_defaultTile; - } - - if(writeAccess) - ensureTileMementoed(col, row, tileHash, tile); - - return tile; -} - -KisTile *KisTiledDataManager::getOldTile(TQ_INT32 col, TQ_INT32 row, KisTile *def) -{ - KisTile *tile = 0; - - // Lookup tile in hash table of current memento - if (m_currentMemento) - { - if (!m_currentMemento->valid()) return def; - //Q_ASSERT(m_currentMemento->valid()); - - TQ_UINT32 tileHash = calcTileHash(col, row); - tile = m_currentMemento->m_hashTable[tileHash]; - while (tile != 0) - { - if (tile->getRow() == row && tile->getCol() == col) - break; - - tile = tile->getNext(); - } - } - - if (!tile) - tile = def; - - return tile; -} - -TQ_UINT8* KisTiledDataManager::pixelPtr(TQ_INT32 x, TQ_INT32 y, bool writable) -{ - // Ahem, this is a bit not as good. The point is, this function needs the tile data, - // but it might be swapped out. This code swaps it in, but at function exit it might - // be swapped out again! THIS MAKES THE RETURNED POINTER QUITE VOLATILE - return pixelPtrSafe(x, y, writable) -> data(); -} - -KisTileDataWrapperSP KisTiledDataManager::pixelPtrSafe(TQ_INT32 x, TQ_INT32 y, bool writable) { - TQ_INT32 row = yToRow(y); - TQ_INT32 col = xToCol(x); - - // calc limits within the tile - TQ_INT32 yInTile = y - row * KisTile::HEIGHT; - TQ_INT32 xInTile = x - col * KisTile::WIDTH; - TQ_INT32 offset = m_pixelSize * (yInTile * KisTile::WIDTH + xInTile); - - KisTile *tile = getTile(col, row, writable); - - return new KisTileDataWrapper(tile, offset); -} - -const TQ_UINT8* KisTiledDataManager::pixel(TQ_INT32 x, TQ_INT32 y) -{ - return pixelPtr(x, y, false); -} - -TQ_UINT8* KisTiledDataManager::writablePixel(TQ_INT32 x, TQ_INT32 y) -{ - return pixelPtr(x, y, true); -} - -void KisTiledDataManager::setPixel(TQ_INT32 x, TQ_INT32 y, const TQ_UINT8 * data) -{ - TQ_UINT8 *pixel = pixelPtr(x, y, true); - memcpy(pixel, data, m_pixelSize); -} - - -void KisTiledDataManager::readBytes(TQ_UINT8 * data, - TQ_INT32 x, TQ_INT32 y, - TQ_INT32 w, TQ_INT32 h) -{ - if (data == 0) return; - //Q_ASSERT(data != 0); - if (w < 0) - w = 0; - - if (h < 0) - h = 0; - - TQ_INT32 dstY = 0; - TQ_INT32 srcY = y; - TQ_INT32 rowsRemaining = h; - - while (rowsRemaining > 0) { - - TQ_INT32 dstX = 0; - TQ_INT32 srcX = x; - TQ_INT32 columnsRemaining = w; - TQ_INT32 numContiguousSrcRows = numContiguousRows(srcY, srcX, srcX + w - 1); - - TQ_INT32 rows = TQMIN(numContiguousSrcRows, rowsRemaining); - - while (columnsRemaining > 0) { - - TQ_INT32 numContiguousSrcColumns = numContiguousColumns(srcX, srcY, srcY + rows - 1); - - TQ_INT32 columns = TQMIN(numContiguousSrcColumns, columnsRemaining); - - KisTileDataWrapperSP tileData = pixelPtrSafe(srcX, srcY, false); - const TQ_UINT8 *srcData = tileData -> data(); - TQ_INT32 srcRowStride = rowStride(srcX, srcY); - - TQ_UINT8 *dstData = data + ((dstX + (dstY * w)) * m_pixelSize); - TQ_INT32 dstRowStride = w * m_pixelSize; - - for (TQ_INT32 row = 0; row < rows; row++) { - memcpy(dstData, srcData, columns * m_pixelSize); - dstData += dstRowStride; - srcData += srcRowStride; - } - - srcX += columns; - dstX += columns; - columnsRemaining -= columns; - } - - srcY += rows; - dstY += rows; - rowsRemaining -= rows; - } - -} - - -void KisTiledDataManager::writeBytes(const TQ_UINT8 * bytes, - TQ_INT32 x, TQ_INT32 y, - TQ_INT32 w, TQ_INT32 h) -{ - if (bytes == 0) return; - //Q_ASSERT(bytes != 0); - - // XXX: Is this correct? - if (w < 0) - w = 0; - - if (h < 0) - h = 0; - - TQ_INT32 srcY = 0; - TQ_INT32 dstY = y; - TQ_INT32 rowsRemaining = h; - - while (rowsRemaining > 0) { - - TQ_INT32 srcX = 0; - TQ_INT32 dstX = x; - TQ_INT32 columnsRemaining = w; - TQ_INT32 numContiguousdstRows = numContiguousRows(dstY, dstX, dstX + w - 1); - - TQ_INT32 rows = TQMIN(numContiguousdstRows, rowsRemaining); - - while (columnsRemaining > 0) { - - TQ_INT32 numContiguousdstColumns = numContiguousColumns(dstX, dstY, dstY + rows - 1); - - TQ_INT32 columns = TQMIN(numContiguousdstColumns, columnsRemaining); - - //TQ_UINT8 *dstData = writablePixel(dstX, dstY); - KisTileDataWrapperSP tileData = pixelPtrSafe(dstX, dstY, true); - TQ_UINT8 *dstData = tileData->data(); - TQ_INT32 dstRowStride = rowStride(dstX, dstY); - - const TQ_UINT8 *srcData = bytes + ((srcX + (srcY * w)) * m_pixelSize); - TQ_INT32 srcRowStride = w * m_pixelSize; - - for (TQ_INT32 row = 0; row < rows; row++) { - memcpy(dstData, srcData, columns * m_pixelSize); - srcData += srcRowStride; - dstData += dstRowStride; - } - - dstX += columns; - srcX += columns; - columnsRemaining -= columns; - } - - dstY += rows; - srcY += rows; - rowsRemaining -= rows; - } -} - -TQ_INT32 KisTiledDataManager::numContiguousColumns(TQ_INT32 x, TQ_INT32 minY, TQ_INT32 maxY) -{ - TQ_INT32 numColumns; - - Q_UNUSED(minY); - Q_UNUSED(maxY); - - if (x >= 0) { - numColumns = KisTile::WIDTH - (x % KisTile::WIDTH); - } else { - numColumns = ((-x - 1) % KisTile::WIDTH) + 1; - } - - return numColumns; -} - -TQ_INT32 KisTiledDataManager::numContiguousRows(TQ_INT32 y, TQ_INT32 minX, TQ_INT32 maxX) -{ - TQ_INT32 numRows; - - Q_UNUSED(minX); - Q_UNUSED(maxX); - - if (y >= 0) { - numRows = KisTile::HEIGHT - (y % KisTile::HEIGHT); - } else { - numRows = ((-y - 1) % KisTile::HEIGHT) + 1; - } - - return numRows; -} - -TQ_INT32 KisTiledDataManager::rowStride(TQ_INT32 x, TQ_INT32 y) -{ - Q_UNUSED(x); - Q_UNUSED(y); - - return KisTile::WIDTH * m_pixelSize; -} - -TQ_INT32 KisTiledDataManager::numTiles(void) const -{ - return m_numTiles; -} - -KisTileDataWrapper::KisTileDataWrapper(KisTile* tile, TQ_INT32 offset) - : m_tile(tile), m_offset(offset) -{ - m_tile->addReader(); -} - -KisTileDataWrapper::~KisTileDataWrapper() -{ - m_tile->removeReader(); -} diff --git a/chalk/core/tiles/kis_tileddatamanager.cpp b/chalk/core/tiles/kis_tileddatamanager.cpp new file mode 100644 index 00000000..4586903c --- /dev/null +++ b/chalk/core/tiles/kis_tileddatamanager.cpp @@ -0,0 +1,1044 @@ +/* + * Copyright (c) 2004 Casper Boemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include + +#include + +#include "kis_global.h" +#include "kis_debug_areas.h" +#include "kis_tileddatamanager.h" +#include "kis_tilediterator.h" +#include "kis_tile.h" +#include "kis_memento.h" +#include "kis_tilemanager.h" + +/* The data area is divided into tiles each say 64x64 pixels (defined at compiletime) + * The tiles are laid out in a matrix that can have negative indexes. + * The matrix grows automatically if needed (a call for writeacces to a tile outside the current extent) + * Even though the matrix has grown it may still not contain tiles at specific positions. They are created on demand + */ + +KisTiledDataManager::KisTiledDataManager(TQ_UINT32 pixelSize, const TQ_UINT8 *defPixel) +{ + m_pixelSize = pixelSize; + + m_defPixel = new TQ_UINT8[m_pixelSize]; + TQ_CHECK_PTR(m_defPixel); + memcpy(m_defPixel, defPixel, m_pixelSize); + + m_defaultTile = new KisTile(pixelSize,0,0, m_defPixel); + TQ_CHECK_PTR(m_defaultTile); + + m_hashTable = new KisTile * [1024]; + TQ_CHECK_PTR(m_hashTable); + + for(int i = 0; i < 1024; i++) + m_hashTable [i] = 0; + m_numTiles = 0; + m_currentMemento = 0; + m_extentMinX = TQ_INT32_MAX; + m_extentMinY = TQ_INT32_MAX; + m_extentMaxX = TQ_INT32_MIN; + m_extentMaxY = TQ_INT32_MIN; +} + +KisTiledDataManager::KisTiledDataManager(const KisTiledDataManager & dm) + : TDEShared() +{ + m_pixelSize = dm.m_pixelSize; + + m_defPixel = new TQ_UINT8[m_pixelSize]; + TQ_CHECK_PTR(m_defPixel); + memcpy(m_defPixel, dm.m_defPixel, m_pixelSize); + + m_defaultTile = new KisTile(*dm.m_defaultTile, dm.m_defaultTile->getCol(), dm.m_defaultTile->getRow()); + TQ_CHECK_PTR(m_defaultTile); + + m_hashTable = new KisTile * [1024]; + TQ_CHECK_PTR(m_hashTable); + + m_numTiles = 0; + m_currentMemento = 0; + m_extentMinX = dm.m_extentMinX; + m_extentMinY = dm.m_extentMinY; + m_extentMaxX = dm.m_extentMaxX; + m_extentMaxY = dm.m_extentMaxY; + + // Deep copy every tile. XXX: Make this copy-on-write! + for(int i = 0; i < 1024; i++) + { + const KisTile *tile = dm.m_hashTable[i]; + + m_hashTable[i] = 0; + + while(tile) + { + KisTile *newtile = new KisTile(*tile, tile->getCol(), tile->getRow()); + TQ_CHECK_PTR(newtile); + + newtile->setNext(m_hashTable[i]); + m_hashTable[i] = newtile; + tile = tile->getNext(); + + m_numTiles++; + } + } + +} + +KisTiledDataManager::~KisTiledDataManager() +{ + // Deep delete every tile + for(int i = 0; i < 1024; i++) + { + const KisTile *tile = m_hashTable[i]; + + while(tile) + { + const KisTile *deltile = tile; + tile = tile->getNext(); + delete deltile; + } + } + delete [] m_hashTable; + delete m_defaultTile; + delete [] m_defPixel; +} + +void KisTiledDataManager::setDefaultPixel(const TQ_UINT8 *defPixel) +{ + if (defPixel == 0) return; + + memcpy(m_defPixel, defPixel, m_pixelSize); + + m_defaultTile->setData(m_defPixel); +} + +bool KisTiledDataManager::write(KoStore *store) +{ + + if (store == 0) return false; + //Q_ASSERT(store != 0); + + char str[80]; + + sprintf(str, "%d\n", m_numTiles); + store->write(str,strlen(str)); + + for(int i = 0; i < 1024; i++) + { + const KisTile *tile = m_hashTable[i]; + + while(tile) + { + sprintf(str, "%d,%d,%d,%d\n", tile->getCol() * KisTile::WIDTH, + tile->getRow() * KisTile::HEIGHT, + KisTile::WIDTH, KisTile::HEIGHT); + store->write(str,strlen(str)); + + tile->addReader(); + store->write((char *)tile->m_data, KisTile::HEIGHT * KisTile::WIDTH * m_pixelSize); + tile->removeReader(); + + tile = tile->getNext(); + } + } + + return true; +} +bool KisTiledDataManager::read(KoStore *store) +{ + if (store == 0) return false; + //Q_ASSERT(store != 0); + + char str[80]; + TQ_INT32 x,y,w,h; + + TQIODevice *stream = store->device(); + if (stream == 0) return false; + //Q_ASSERT(stream != 0); + + stream->readLine(str, 79); + + sscanf(str,"%u",&m_numTiles); + + for(TQ_UINT32 i = 0; i < m_numTiles; i++) + { + stream->readLine(str, 79); + sscanf(str,"%d,%d,%d,%d",&x,&y,&w,&h); + + // the following is only correct as long as tile size is not changed + // The first time we change tilesize the dimensions just read needs to be respected + // but for now we just assume that tiles are the same size as ever. + TQ_INT32 row = yToRow(y); + TQ_INT32 col = xToCol(x); + TQ_UINT32 tileHash = calcTileHash(col, row); + + KisTile *tile = new KisTile(m_pixelSize, col, row, m_defPixel); + TQ_CHECK_PTR(tile); + + updateExtent(col,row); + + tile->addReader(); + store->read((char *)tile->m_data, KisTile::HEIGHT * KisTile::WIDTH * m_pixelSize); + tile->removeReader(); + + tile->setNext(m_hashTable[tileHash]); + m_hashTable[tileHash] = tile; + } + return true; +} + +void KisTiledDataManager::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const +{ + x = m_extentMinX; + y = m_extentMinY; + + if (m_extentMaxX >= m_extentMinX) { + w = m_extentMaxX - m_extentMinX + 1; + } else { + w = 0; + } + + if (m_extentMaxY >= m_extentMinY) { + h = m_extentMaxY - m_extentMinY + 1; + } else { + h = 0; + } +} + +TQRect KisTiledDataManager::extent() const +{ + TQ_INT32 x; + TQ_INT32 y; + TQ_INT32 w; + TQ_INT32 h; + + extent(x, y, w, h); + + return TQRect(x, y, w, h); +} + +void KisTiledDataManager::setExtent(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h) +{ + TQRect newRect = TQRect(x, y, w, h).normalize(); + //printRect("newRect", newRect); + TQRect oldRect = TQRect(m_extentMinX, m_extentMinY, m_extentMaxX - m_extentMinX + 1, m_extentMaxY - m_extentMinY + 1).normalize(); + //printRect("oldRect", oldRect); + + // Do nothing if the desired size is bigger than we currently are: that is handled by the autoextending automatically + if (newRect.contains(oldRect)) return; + + // Loop through all tiles, if a tile is wholly outside the extent, add to the memento, then delete it, + // if the tile is partially outside the extent, clear the outside pixels to the default pixel. + for(int tileHash = 0; tileHash < 1024; tileHash++) + { + KisTile *tile = m_hashTable[tileHash]; + KisTile *previousTile = 0; + + while(tile) + { + TQRect tileRect = TQRect(tile->getCol() * KisTile::WIDTH, tile->getRow() * KisTile::HEIGHT, KisTile::WIDTH, KisTile::HEIGHT); + //printRect("tileRect", tileRect); + + if (newRect.contains(tileRect)) { + // Completely inside, do nothing + previousTile = tile; + tile = tile->getNext(); + } + else { + ensureTileMementoed(tile->getCol(), tile->getRow(), tileHash, tile); + + if (newRect.intersects(tileRect)) { + + // Create the intersection of the tile and new rect + TQRect intersection = newRect.intersect(tileRect); + //printRect("intersection", intersection); + intersection.setRect(intersection.x() - tileRect.x(), intersection.y() - tileRect.y(), intersection.width(), intersection.height()); + + // This can be done a lot more efficiently, no doubt, by clearing runs of pixels to the left and the right of + // the intersecting line. + tile->addReader(); + for (int y = 0; y < KisTile::HEIGHT; ++y) { + for (int x = 0; x < KisTile::WIDTH; ++x) { + if (!intersection.contains(x,y)) { + TQ_UINT8 * ptr = tile->data(x, y); + memcpy(ptr, m_defPixel, m_pixelSize); + } + } + } + tile->removeReader(); + previousTile = tile; + tile = tile->getNext(); + } + else { + KisTile *deltile = tile; + tile = tile->getNext(); + + m_numTiles--; + + if (previousTile) + previousTile->setNext(tile); + else + m_hashTable[tileHash] = tile; + delete deltile; + } + } + } + } + + // Set the extent correctly + m_extentMinX = x; + m_extentMinY = y; + m_extentMaxX = x + w - 1; + m_extentMaxY = y + h - 1; +} + +void KisTiledDataManager::recalculateExtent() +{ + m_extentMinX = TQ_INT32_MAX; + m_extentMinY = TQ_INT32_MAX; + m_extentMaxX = TQ_INT32_MIN; + m_extentMaxY = TQ_INT32_MIN; + + // Loop through all tiles. + for (int tileHash = 0; tileHash < 1024; tileHash++) + { + const KisTile *tile = m_hashTable[tileHash]; + + while (tile) + { + updateExtent(tile->getCol(), tile->getRow()); + tile = tile->getNext(); + } + } +} + +void KisTiledDataManager::clear(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, TQ_UINT8 clearValue) +{ + if (w < 1 || h < 1) { + return; + } + + TQ_INT32 firstColumn = xToCol(x); + TQ_INT32 lastColumn = xToCol(x + w - 1); + + TQ_INT32 firstRow = yToRow(y); + TQ_INT32 lastRow = yToRow(y + h - 1); + + TQRect clearRect(x, y, w, h); + + const TQ_UINT32 rowStride = KisTile::WIDTH * m_pixelSize; + + for (TQ_INT32 row = firstRow; row <= lastRow; ++row) { + for (TQ_INT32 column = firstColumn; column <= lastColumn; ++column) { + + KisTile *tile = getTile(column, row, true); + TQRect tileRect = tile->extent(); + + TQRect clearTileRect = clearRect & tileRect; + + tile->addReader(); + if (clearTileRect == tileRect) { + // Clear whole tile + memset(tile->data(), clearValue, KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize); + } else { + + TQ_UINT32 rowsRemaining = clearTileRect.height(); + TQ_UINT8 *dst = tile->data(clearTileRect.x() - tileRect.x(), clearTileRect.y() - tileRect.y()); + + while (rowsRemaining > 0) { + memset(dst, clearValue, clearTileRect.width() * m_pixelSize); + dst += rowStride; + --rowsRemaining; + } + } + tile->removeReader(); + } + } +} + +void KisTiledDataManager::clear(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const TQ_UINT8 *clearPixel) +{ + Q_ASSERT(clearPixel != 0); + + if (clearPixel == 0 || w < 1 || h < 1) { + return; + } + + bool pixelBytesAreTheSame = true; + + for (TQ_UINT32 i = 0; i < m_pixelSize; ++i) { + if (clearPixel[i] != clearPixel[0]) { + pixelBytesAreTheSame = false; + break; + } + } + + if (pixelBytesAreTheSame) { + clear(x, y, w, h, clearPixel[0]); + } else { + + TQ_INT32 firstColumn = xToCol(x); + TQ_INT32 lastColumn = xToCol(x + w - 1); + + TQ_INT32 firstRow = yToRow(y); + TQ_INT32 lastRow = yToRow(y + h - 1); + + TQRect clearRect(x, y, w, h); + + const TQ_UINT32 rowStride = KisTile::WIDTH * m_pixelSize; + + TQ_UINT8 *clearPixelData = 0; + + if (w >= KisTile::WIDTH && h >= KisTile::HEIGHT) { + + // There might be a whole tile to be cleared so generate a cleared tile. + clearPixelData = new TQ_UINT8[KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize]; + + TQ_UINT8 *dst = clearPixelData; + TQ_UINT32 pixelsRemaining = KisTile::WIDTH; + + // Generate one row + while (pixelsRemaining > 0) { + memcpy(dst, clearPixel, m_pixelSize); + dst += m_pixelSize; + --pixelsRemaining; + } + + TQ_UINT32 rowsRemaining = KisTile::HEIGHT - 1; + + // Copy to the rest of the rows. + while (rowsRemaining > 0) { + memcpy(dst, clearPixelData, rowStride); + dst += rowStride; + --rowsRemaining; + } + + } else { + + // Generate one row + TQ_UINT32 maxRunLength = TQMIN(w, KisTile::WIDTH); + + clearPixelData = new TQ_UINT8[maxRunLength * m_pixelSize]; + + TQ_UINT8 *dst = clearPixelData; + TQ_UINT32 pixelsRemaining = maxRunLength; + + while (pixelsRemaining > 0) { + memcpy(dst, clearPixel, m_pixelSize); + dst += m_pixelSize; + --pixelsRemaining; + } + } + + for (TQ_INT32 row = firstRow; row <= lastRow; ++row) { + for (TQ_INT32 column = firstColumn; column <= lastColumn; ++column) { + + KisTile *tile = getTile(column, row, true); + TQRect tileRect = tile->extent(); + + TQRect clearTileRect = clearRect & tileRect; + + if (clearTileRect == tileRect) { + + // Clear whole tile + tile->addReader(); + memcpy(tile->data(), clearPixelData, KisTile::WIDTH * KisTile::HEIGHT * m_pixelSize); + tile->removeReader(); + } else { + + TQ_UINT32 rowsRemaining = clearTileRect.height(); + tile->addReader(); + TQ_UINT8 *dst = tile->data(clearTileRect.x() - tileRect.x(), clearTileRect.y() - tileRect.y()); + + while (rowsRemaining > 0) { + memcpy(dst, clearPixelData, clearTileRect.width() * m_pixelSize); + dst += rowStride; + --rowsRemaining; + } + tile->removeReader(); + } + } + } + + delete [] clearPixelData; + } +} + +void KisTiledDataManager::clear() +{ + // Loop through all tiles, add to the memento, then delete it, + for(int tileHash = 0; tileHash < 1024; tileHash++) + { + const KisTile *tile = m_hashTable[tileHash]; + + while(tile) + { + ensureTileMementoed(tile->getCol(), tile->getRow(), tileHash, tile); + + const KisTile *deltile = tile; + tile = tile->getNext(); + + delete deltile; + } + m_hashTable[tileHash] = 0; + } + + m_numTiles = 0; + + // Set the extent correctly + m_extentMinX = TQ_INT32_MAX; + m_extentMinY = TQ_INT32_MAX; + m_extentMaxX = TQ_INT32_MIN; + m_extentMaxY = TQ_INT32_MIN; +} + +void KisTiledDataManager::paste(KisDataManagerSP data, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 dx, TQ_INT32 dy, + TQ_INT32 w, TQ_INT32 h) +{ + //CBR_MISSING + sx=sy=dx=dy=w=h;data=0; +} + + +TQ_UINT32 KisTiledDataManager::calcTileHash(TQ_INT32 col, TQ_INT32 row) +{ + return ((row << 5) + (col & 0x1F)) & 0x3FF; +} + +KisMementoSP KisTiledDataManager::getMemento() +{ + m_currentMemento = new KisMemento(m_pixelSize); + TQ_CHECK_PTR(m_currentMemento); + + memcpy(m_currentMemento->m_defPixel, m_defPixel, m_pixelSize); + + return m_currentMemento; +} + +void KisTiledDataManager::rollback(KisMementoSP memento) +{ + if (memento == 0) return; + //Q_ASSERT(memento != 0); + + if (m_currentMemento != 0) { + // Undo means our current memento is no longer valid so remove it. + m_currentMemento = 0; + } + + // Rollback means restoring all of the tiles in the memento to our hashtable. + + // But first clear the memento redo hashtable. + // This is nessesary as new changes might have been done since last rollback (automatic filters) + for(int i = 0; i < 1024; i++) + { + memento->deleteAll(memento->m_redoHashTable[i]); + memento->m_redoHashTable[i]=0; + } + + // Also clear the table of deleted tiles + memento->clearTilesToDeleteOnRedo(); + + // Now on to the real rollback + + memcpy(memento->m_redoDefPixel, m_defPixel, m_pixelSize); + setDefaultPixel(memento->m_defPixel); + + for(int i = 0; i < 1024; i++) + { + KisTile *tile = memento->m_hashTable[i]; + + while(tile) + { + // The memento has a tile stored that we need to roll back + // Now find the corresponding one in our hashtable + KisTile *curTile = m_hashTable[i]; + KisTile *preTile = 0; + while(curTile) + { + if(curTile->getRow() == tile->getRow() && curTile->getCol() == tile->getCol()) + { + break; + } + preTile = curTile; + curTile = curTile->getNext(); + } + + if(curTile) + { + // Remove it from our hashtable + if(preTile) + preTile->setNext(curTile->getNext()); + else + m_hashTable[i]= curTile->getNext(); + + m_numTiles--; + + // And put it in the redo hashtable of the memento + curTile->setNext(memento->m_redoHashTable[i]); + memento->m_redoHashTable[i] = curTile; + } + else + { + memento->addTileToDeleteOnRedo(tile->getCol(), tile->getRow()); + // As we are pratically adding a new tile we need to update the extent + updateExtent(tile->getCol(), tile->getRow()); + } + + // Put a copy of the memento tile into our hashtable + curTile = new KisTile(*tile); + TQ_CHECK_PTR(curTile); + m_numTiles++; + + curTile->setNext(m_hashTable[i]); + m_hashTable[i] = curTile; + + tile = tile->getNext(); + } + } + + if (memento->tileListToDeleteOnUndo() != 0) { + // XXX: We currently add these tiles above, only to delete them again here. + deleteTiles(memento->tileListToDeleteOnUndo()); + } +} + +void KisTiledDataManager::rollforward(KisMementoSP memento) +{ + if (memento == 0) return; + //Q_ASSERT(memento != 0); + + if (m_currentMemento != 0) { + // Redo means our current memento is no longer valid so remove it. + m_currentMemento = 0; + } + + // Rollforward means restoring all of the tiles in the memento's redo to our hashtable. + + setDefaultPixel(memento->m_redoDefPixel); + + for(int i = 0; i < 1024; i++) + { + KisTile *tile = memento->m_redoHashTable[i]; + + while(tile) + { + // The memento has a tile stored that we need to roll forward + // Now find the corresponding one in our hashtable + KisTile *curTile = m_hashTable[i]; + KisTile *preTile = 0; + while(curTile) + { + if(curTile->getRow() == tile->getRow() && curTile->getCol() == tile->getCol()) + { + break; + } + preTile = curTile; + curTile = curTile->getNext(); + } + + if (curTile) + { + // Remove it from our hashtable + if(preTile) + preTile->setNext(curTile->getNext()); + else + m_hashTable[i]= curTile->getNext(); + + // And delete it (it's equal to the one stored in the memento's undo) + m_numTiles--; + delete curTile; + } + + // Put a copy of the memento tile into our hashtable + curTile = new KisTile(*tile); + TQ_CHECK_PTR(curTile); + + curTile->setNext(m_hashTable[i]); + m_hashTable[i] = curTile; + m_numTiles++; + updateExtent(curTile->getCol(), curTile->getRow()); + + tile = tile->getNext(); + } + } + + // Roll forward also means re-deleting the tiles that was deleted but restored by the undo + if (memento->tileListToDeleteOnRedo() != 0) { + deleteTiles(memento->tileListToDeleteOnRedo()); + } +} + +void KisTiledDataManager::deleteTiles(const KisMemento::DeletedTile *d) +{ + while (d) + { + TQ_UINT32 tileHash = calcTileHash(d->col(), d->row()); + KisTile *curTile = m_hashTable[tileHash]; + KisTile *preTile = 0; + while(curTile) + { + if(curTile->getRow() == d->row() && curTile->getCol() == d->col()) + { + break; + } + preTile = curTile; + curTile = curTile->getNext(); + } + if (curTile) { + // Remove it from our hashtable + if(preTile) + preTile->setNext(curTile->getNext()); + else + m_hashTable[tileHash] = curTile->getNext(); + + // And delete it (it's equal to the one stored in the memento's undo) + m_numTiles--; + delete curTile; + } + d = d->next(); + } + + recalculateExtent(); +} + +void KisTiledDataManager::ensureTileMementoed(TQ_INT32 col, TQ_INT32 row, TQ_UINT32 tileHash, const KisTile *refTile) +{ + if (refTile == 0) return; + //Q_ASSERT(refTile != 0); + + // Basically we search for the tile in the current memento, and if it's already there we do nothing, otherwise + // we make a copy of the tile and put it in the current memento + + if(!m_currentMemento) + return; + + KisTile *tile = m_currentMemento->m_hashTable[tileHash]; + while(tile != 0) + { + if(tile->getRow() == row && tile->getCol() == col) + break; + + tile = tile->getNext(); + } + if(tile) + return; // it has allready been stored + + tile = new KisTile(*refTile); + TQ_CHECK_PTR(tile); + + tile->setNext(m_currentMemento->m_hashTable[tileHash]); + m_currentMemento->m_hashTable[tileHash] = tile; + m_currentMemento->m_numTiles++; +} + +void KisTiledDataManager::updateExtent(TQ_INT32 col, TQ_INT32 row) +{ + if(m_extentMinX > col * KisTile::WIDTH) + m_extentMinX = col * KisTile::WIDTH; + if(m_extentMaxX < (col+1) * KisTile::WIDTH - 1) + m_extentMaxX = (col+1) * KisTile::WIDTH - 1; + if(m_extentMinY > row * KisTile::HEIGHT) + m_extentMinY = row * KisTile::HEIGHT; + if(m_extentMaxY < (row+1) * KisTile::HEIGHT - 1) + m_extentMaxY = (row+1) * KisTile::HEIGHT - 1; +} + +KisTile *KisTiledDataManager::getTile(TQ_INT32 col, TQ_INT32 row, bool writeAccess) +{ + TQ_UINT32 tileHash = calcTileHash(col, row); + + // Lookup tile in hash table + KisTile *tile = m_hashTable[tileHash]; + while(tile != 0) + { + if(tile->getRow() == row && tile->getCol() == col) + break; + + tile = tile->getNext(); + } + + // Might not have been created yet + if(!tile) + { + if(writeAccess) + { + // Create a new tile + tile = new KisTile(*m_defaultTile, col, row); + TQ_CHECK_PTR(tile); + + tile->setNext(m_hashTable[tileHash]); + m_hashTable[tileHash] = tile; + m_numTiles++; + updateExtent(col, row); + + if (m_currentMemento && !m_currentMemento->containsTile(col, row, tileHash)) { + m_currentMemento->addTileToDeleteOnUndo(col, row); + } + } + else + // If only read access then it's enough to share a default tile + tile = m_defaultTile; + } + + if(writeAccess) + ensureTileMementoed(col, row, tileHash, tile); + + return tile; +} + +KisTile *KisTiledDataManager::getOldTile(TQ_INT32 col, TQ_INT32 row, KisTile *def) +{ + KisTile *tile = 0; + + // Lookup tile in hash table of current memento + if (m_currentMemento) + { + if (!m_currentMemento->valid()) return def; + //Q_ASSERT(m_currentMemento->valid()); + + TQ_UINT32 tileHash = calcTileHash(col, row); + tile = m_currentMemento->m_hashTable[tileHash]; + while (tile != 0) + { + if (tile->getRow() == row && tile->getCol() == col) + break; + + tile = tile->getNext(); + } + } + + if (!tile) + tile = def; + + return tile; +} + +TQ_UINT8* KisTiledDataManager::pixelPtr(TQ_INT32 x, TQ_INT32 y, bool writable) +{ + // Ahem, this is a bit not as good. The point is, this function needs the tile data, + // but it might be swapped out. This code swaps it in, but at function exit it might + // be swapped out again! THIS MAKES THE RETURNED POINTER QUITE VOLATILE + return pixelPtrSafe(x, y, writable) -> data(); +} + +KisTileDataWrapperSP KisTiledDataManager::pixelPtrSafe(TQ_INT32 x, TQ_INT32 y, bool writable) { + TQ_INT32 row = yToRow(y); + TQ_INT32 col = xToCol(x); + + // calc limits within the tile + TQ_INT32 yInTile = y - row * KisTile::HEIGHT; + TQ_INT32 xInTile = x - col * KisTile::WIDTH; + TQ_INT32 offset = m_pixelSize * (yInTile * KisTile::WIDTH + xInTile); + + KisTile *tile = getTile(col, row, writable); + + return new KisTileDataWrapper(tile, offset); +} + +const TQ_UINT8* KisTiledDataManager::pixel(TQ_INT32 x, TQ_INT32 y) +{ + return pixelPtr(x, y, false); +} + +TQ_UINT8* KisTiledDataManager::writablePixel(TQ_INT32 x, TQ_INT32 y) +{ + return pixelPtr(x, y, true); +} + +void KisTiledDataManager::setPixel(TQ_INT32 x, TQ_INT32 y, const TQ_UINT8 * data) +{ + TQ_UINT8 *pixel = pixelPtr(x, y, true); + memcpy(pixel, data, m_pixelSize); +} + + +void KisTiledDataManager::readBytes(TQ_UINT8 * data, + TQ_INT32 x, TQ_INT32 y, + TQ_INT32 w, TQ_INT32 h) +{ + if (data == 0) return; + //Q_ASSERT(data != 0); + if (w < 0) + w = 0; + + if (h < 0) + h = 0; + + TQ_INT32 dstY = 0; + TQ_INT32 srcY = y; + TQ_INT32 rowsRemaining = h; + + while (rowsRemaining > 0) { + + TQ_INT32 dstX = 0; + TQ_INT32 srcX = x; + TQ_INT32 columnsRemaining = w; + TQ_INT32 numContiguousSrcRows = numContiguousRows(srcY, srcX, srcX + w - 1); + + TQ_INT32 rows = TQMIN(numContiguousSrcRows, rowsRemaining); + + while (columnsRemaining > 0) { + + TQ_INT32 numContiguousSrcColumns = numContiguousColumns(srcX, srcY, srcY + rows - 1); + + TQ_INT32 columns = TQMIN(numContiguousSrcColumns, columnsRemaining); + + KisTileDataWrapperSP tileData = pixelPtrSafe(srcX, srcY, false); + const TQ_UINT8 *srcData = tileData -> data(); + TQ_INT32 srcRowStride = rowStride(srcX, srcY); + + TQ_UINT8 *dstData = data + ((dstX + (dstY * w)) * m_pixelSize); + TQ_INT32 dstRowStride = w * m_pixelSize; + + for (TQ_INT32 row = 0; row < rows; row++) { + memcpy(dstData, srcData, columns * m_pixelSize); + dstData += dstRowStride; + srcData += srcRowStride; + } + + srcX += columns; + dstX += columns; + columnsRemaining -= columns; + } + + srcY += rows; + dstY += rows; + rowsRemaining -= rows; + } + +} + + +void KisTiledDataManager::writeBytes(const TQ_UINT8 * bytes, + TQ_INT32 x, TQ_INT32 y, + TQ_INT32 w, TQ_INT32 h) +{ + if (bytes == 0) return; + //Q_ASSERT(bytes != 0); + + // XXX: Is this correct? + if (w < 0) + w = 0; + + if (h < 0) + h = 0; + + TQ_INT32 srcY = 0; + TQ_INT32 dstY = y; + TQ_INT32 rowsRemaining = h; + + while (rowsRemaining > 0) { + + TQ_INT32 srcX = 0; + TQ_INT32 dstX = x; + TQ_INT32 columnsRemaining = w; + TQ_INT32 numContiguousdstRows = numContiguousRows(dstY, dstX, dstX + w - 1); + + TQ_INT32 rows = TQMIN(numContiguousdstRows, rowsRemaining); + + while (columnsRemaining > 0) { + + TQ_INT32 numContiguousdstColumns = numContiguousColumns(dstX, dstY, dstY + rows - 1); + + TQ_INT32 columns = TQMIN(numContiguousdstColumns, columnsRemaining); + + //TQ_UINT8 *dstData = writablePixel(dstX, dstY); + KisTileDataWrapperSP tileData = pixelPtrSafe(dstX, dstY, true); + TQ_UINT8 *dstData = tileData->data(); + TQ_INT32 dstRowStride = rowStride(dstX, dstY); + + const TQ_UINT8 *srcData = bytes + ((srcX + (srcY * w)) * m_pixelSize); + TQ_INT32 srcRowStride = w * m_pixelSize; + + for (TQ_INT32 row = 0; row < rows; row++) { + memcpy(dstData, srcData, columns * m_pixelSize); + srcData += srcRowStride; + dstData += dstRowStride; + } + + dstX += columns; + srcX += columns; + columnsRemaining -= columns; + } + + dstY += rows; + srcY += rows; + rowsRemaining -= rows; + } +} + +TQ_INT32 KisTiledDataManager::numContiguousColumns(TQ_INT32 x, TQ_INT32 minY, TQ_INT32 maxY) +{ + TQ_INT32 numColumns; + + Q_UNUSED(minY); + Q_UNUSED(maxY); + + if (x >= 0) { + numColumns = KisTile::WIDTH - (x % KisTile::WIDTH); + } else { + numColumns = ((-x - 1) % KisTile::WIDTH) + 1; + } + + return numColumns; +} + +TQ_INT32 KisTiledDataManager::numContiguousRows(TQ_INT32 y, TQ_INT32 minX, TQ_INT32 maxX) +{ + TQ_INT32 numRows; + + Q_UNUSED(minX); + Q_UNUSED(maxX); + + if (y >= 0) { + numRows = KisTile::HEIGHT - (y % KisTile::HEIGHT); + } else { + numRows = ((-y - 1) % KisTile::HEIGHT) + 1; + } + + return numRows; +} + +TQ_INT32 KisTiledDataManager::rowStride(TQ_INT32 x, TQ_INT32 y) +{ + Q_UNUSED(x); + Q_UNUSED(y); + + return KisTile::WIDTH * m_pixelSize; +} + +TQ_INT32 KisTiledDataManager::numTiles(void) const +{ + return m_numTiles; +} + +KisTileDataWrapper::KisTileDataWrapper(KisTile* tile, TQ_INT32 offset) + : m_tile(tile), m_offset(offset) +{ + m_tile->addReader(); +} + +KisTileDataWrapper::~KisTileDataWrapper() +{ + m_tile->removeReader(); +} diff --git a/chalk/core/tiles/kis_tiledhlineiterator.cc b/chalk/core/tiles/kis_tiledhlineiterator.cc deleted file mode 100644 index cf023c1e..00000000 --- a/chalk/core/tiles/kis_tiledhlineiterator.cc +++ /dev/null @@ -1,213 +0,0 @@ -/* - * This file is part of the KDE project - * - * Copyright (c) 2004 Casper Boemann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -#include - -#include "kis_tile_global.h" -#include "kis_tilediterator.h" - -KisTiledHLineIterator::KisTiledHLineIterator( KisTiledDataManager *ndevice, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable) : - KisTiledIterator(ndevice), - m_right(x+w-1), m_left(x) -{ - Q_ASSERT(ndevice != 0); - - m_writable = writable; - m_x = x; - m_y = y; - - // Find tile row,col matching x,y - m_row = yToRow(m_y); - m_leftCol = xToCol(m_x); - m_rightCol = xToCol(m_right); - m_col = m_leftCol; - - // calc limits within the tile - m_yInTile = m_y - m_row * KisTile::HEIGHT; - m_leftInTile = m_x - m_leftCol * KisTile::WIDTH; - - if(m_col == m_rightCol) - m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; - else - m_rightInTile = KisTile::WIDTH - 1; - - m_xInTile = m_leftInTile; - - fetchTileData(m_col, m_row); - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); -} - -KisTiledHLineIterator::KisTiledHLineIterator(const KisTiledHLineIterator& rhs) - : KisTiledIterator(rhs) -{ - if (this != &rhs) { - m_right = rhs.m_right; - m_left = rhs.m_left; - m_leftCol = rhs.m_leftCol; - m_rightCol = rhs.m_rightCol; - m_xInTile = rhs.m_xInTile; - m_yInTile = rhs.m_yInTile; - m_leftInTile = rhs.m_leftInTile; - m_rightInTile = rhs.m_rightInTile; - } -} - -KisTiledHLineIterator& KisTiledHLineIterator::operator=(const KisTiledHLineIterator& rhs) -{ - if (this != &rhs) { - KisTiledIterator::operator=(rhs); - m_right = rhs.m_right; - m_left = rhs.m_left; - m_leftCol = rhs.m_leftCol; - m_rightCol = rhs.m_rightCol; - m_xInTile = rhs.m_xInTile; - m_yInTile = rhs.m_yInTile; - m_leftInTile = rhs.m_leftInTile; - m_rightInTile = rhs.m_rightInTile; - } - return *this; -} - -KisTiledHLineIterator::~KisTiledHLineIterator( ) -{ -} - -KisTiledHLineIterator & KisTiledHLineIterator::operator ++ () -{ - if(m_xInTile >= m_rightInTile) - { - nextTile(); - fetchTileData(m_col, m_row); - m_xInTile =m_leftInTile; - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); - } - else - { - m_xInTile++; - m_offset += m_pixelSize; - } - m_x++; - - return *this; -} - -void KisTiledHLineIterator::nextTile() -{ - if(m_col < m_rightCol) - { - m_col++; - m_leftInTile = 0; - - if(m_col == m_rightCol) - m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; - else - m_rightInTile = KisTile::WIDTH - 1; - } -} - -void KisTiledHLineIterator::prevTile() -{ - if(m_col > m_leftCol) - { - m_col--; - - if(m_col == m_leftCol) { - m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; - } else { - m_leftInTile = 0; - } - // the only place this doesn't apply, is if we're in rightCol, and we can't go there - m_rightInTile = KisTile::WIDTH - 1; - } -} - -TQ_INT32 KisTiledHLineIterator::nConseqHPixels() const -{ - return m_rightInTile - m_xInTile + 1; -} - -KisTiledHLineIterator & KisTiledHLineIterator::operator+=(int n) -{ - // XXX what if outside the valid range of this iterator? - if(m_xInTile + n > m_rightInTile) - { - m_x += n; - m_col = xToCol(m_x); - m_xInTile = m_x - m_col * KisTile::WIDTH; - m_leftInTile = 0; - - if(m_col == m_rightCol) - m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; - else - m_rightInTile = KisTile::WIDTH - 1; - - fetchTileData(m_col, m_row); - } - else - { - m_xInTile += n; - m_x += n; - } - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); - - return *this; -} - -KisTiledHLineIterator & KisTiledHLineIterator::operator -- () -{ - if(m_xInTile <= 0) - { - prevTile(); - fetchTileData(m_col, m_row); - m_xInTile = KisTile::WIDTH - 1; - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); - } - else - { - m_xInTile--; - m_offset -= m_pixelSize; - } - m_x--; - - return *this; -} - -void KisTiledHLineIterator::nextRow() -{ - m_y++; - m_yInTile++; - m_x = m_left; - m_leftInTile = m_x - m_leftCol * KisTile::WIDTH; - m_xInTile = m_leftInTile; - if( m_yInTile >= KisTile::HEIGHT ) - { // Need a new row - m_yInTile = 0; - m_row++; - m_col = m_leftCol; - fetchTileData(m_col, m_row); - } else if( m_leftCol != m_col ) { - m_col = m_leftCol; - fetchTileData(m_col, m_row); - } - if(m_col == m_rightCol) - m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; - else - m_rightInTile = KisTile::WIDTH - 1; - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); -} diff --git a/chalk/core/tiles/kis_tiledhlineiterator.cpp b/chalk/core/tiles/kis_tiledhlineiterator.cpp new file mode 100644 index 00000000..cf023c1e --- /dev/null +++ b/chalk/core/tiles/kis_tiledhlineiterator.cpp @@ -0,0 +1,213 @@ +/* + * This file is part of the KDE project + * + * Copyright (c) 2004 Casper Boemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include + +#include "kis_tile_global.h" +#include "kis_tilediterator.h" + +KisTiledHLineIterator::KisTiledHLineIterator( KisTiledDataManager *ndevice, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable) : + KisTiledIterator(ndevice), + m_right(x+w-1), m_left(x) +{ + Q_ASSERT(ndevice != 0); + + m_writable = writable; + m_x = x; + m_y = y; + + // Find tile row,col matching x,y + m_row = yToRow(m_y); + m_leftCol = xToCol(m_x); + m_rightCol = xToCol(m_right); + m_col = m_leftCol; + + // calc limits within the tile + m_yInTile = m_y - m_row * KisTile::HEIGHT; + m_leftInTile = m_x - m_leftCol * KisTile::WIDTH; + + if(m_col == m_rightCol) + m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + + m_xInTile = m_leftInTile; + + fetchTileData(m_col, m_row); + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} + +KisTiledHLineIterator::KisTiledHLineIterator(const KisTiledHLineIterator& rhs) + : KisTiledIterator(rhs) +{ + if (this != &rhs) { + m_right = rhs.m_right; + m_left = rhs.m_left; + m_leftCol = rhs.m_leftCol; + m_rightCol = rhs.m_rightCol; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_leftInTile = rhs.m_leftInTile; + m_rightInTile = rhs.m_rightInTile; + } +} + +KisTiledHLineIterator& KisTiledHLineIterator::operator=(const KisTiledHLineIterator& rhs) +{ + if (this != &rhs) { + KisTiledIterator::operator=(rhs); + m_right = rhs.m_right; + m_left = rhs.m_left; + m_leftCol = rhs.m_leftCol; + m_rightCol = rhs.m_rightCol; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_leftInTile = rhs.m_leftInTile; + m_rightInTile = rhs.m_rightInTile; + } + return *this; +} + +KisTiledHLineIterator::~KisTiledHLineIterator( ) +{ +} + +KisTiledHLineIterator & KisTiledHLineIterator::operator ++ () +{ + if(m_xInTile >= m_rightInTile) + { + nextTile(); + fetchTileData(m_col, m_row); + m_xInTile =m_leftInTile; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + } + else + { + m_xInTile++; + m_offset += m_pixelSize; + } + m_x++; + + return *this; +} + +void KisTiledHLineIterator::nextTile() +{ + if(m_col < m_rightCol) + { + m_col++; + m_leftInTile = 0; + + if(m_col == m_rightCol) + m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + } +} + +void KisTiledHLineIterator::prevTile() +{ + if(m_col > m_leftCol) + { + m_col--; + + if(m_col == m_leftCol) { + m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; + } else { + m_leftInTile = 0; + } + // the only place this doesn't apply, is if we're in rightCol, and we can't go there + m_rightInTile = KisTile::WIDTH - 1; + } +} + +TQ_INT32 KisTiledHLineIterator::nConseqHPixels() const +{ + return m_rightInTile - m_xInTile + 1; +} + +KisTiledHLineIterator & KisTiledHLineIterator::operator+=(int n) +{ + // XXX what if outside the valid range of this iterator? + if(m_xInTile + n > m_rightInTile) + { + m_x += n; + m_col = xToCol(m_x); + m_xInTile = m_x - m_col * KisTile::WIDTH; + m_leftInTile = 0; + + if(m_col == m_rightCol) + m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + + fetchTileData(m_col, m_row); + } + else + { + m_xInTile += n; + m_x += n; + } + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + + return *this; +} + +KisTiledHLineIterator & KisTiledHLineIterator::operator -- () +{ + if(m_xInTile <= 0) + { + prevTile(); + fetchTileData(m_col, m_row); + m_xInTile = KisTile::WIDTH - 1; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + } + else + { + m_xInTile--; + m_offset -= m_pixelSize; + } + m_x--; + + return *this; +} + +void KisTiledHLineIterator::nextRow() +{ + m_y++; + m_yInTile++; + m_x = m_left; + m_leftInTile = m_x - m_leftCol * KisTile::WIDTH; + m_xInTile = m_leftInTile; + if( m_yInTile >= KisTile::HEIGHT ) + { // Need a new row + m_yInTile = 0; + m_row++; + m_col = m_leftCol; + fetchTileData(m_col, m_row); + } else if( m_leftCol != m_col ) { + m_col = m_leftCol; + fetchTileData(m_col, m_row); + } + if(m_col == m_rightCol) + m_rightInTile = m_right - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} diff --git a/chalk/core/tiles/kis_tilediterator.cc b/chalk/core/tiles/kis_tilediterator.cc deleted file mode 100644 index 0f3bdf00..00000000 --- a/chalk/core/tiles/kis_tilediterator.cc +++ /dev/null @@ -1,131 +0,0 @@ -/* - * This file is part of the Chalk - * - * Copyright (c) 2004 Casper Boemann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -#include - -#include "kis_tile_global.h" -#include "kis_tilediterator.h" - -KisTiledIterator::KisTiledIterator( KisTiledDataManager *ndevice) -{ - Q_ASSERT(ndevice != 0); - m_ktm = ndevice; - m_x = 0; - m_y = 0; - m_row = 0; - m_col = 0; - m_pixelSize = m_ktm->pixelSize(); - m_tile = 0; - m_oldTile = 0; -} - -KisTiledIterator::~KisTiledIterator( ) -{ - if (m_tile) - m_tile->removeReader(); - if (m_oldTile) - m_oldTile->removeReader(); -} - -KisTiledIterator::KisTiledIterator(const KisTiledIterator& rhs) - : TDEShared() -{ - if (this != &rhs) { - m_ktm = rhs.m_ktm; - m_pixelSize = rhs.m_pixelSize; - m_x = rhs.m_x; - m_y = rhs.m_y; - m_row = rhs.m_row; - m_col = rhs.m_col; - m_data = rhs.m_data; - m_oldData = rhs.m_oldData; - m_offset = rhs.m_offset; - m_tile = rhs.m_tile; - m_oldTile = rhs.m_oldTile; - m_writable = rhs.m_writable; - if (m_tile) - m_tile->addReader(); - } -} - -KisTiledIterator& KisTiledIterator::operator=(const KisTiledIterator& rhs) -{ - if (this != &rhs) { - if (m_tile) - m_tile->removeReader(); - if (m_oldTile) - m_oldTile->removeReader(); - m_ktm = rhs.m_ktm; - m_pixelSize = rhs.m_pixelSize; - m_x = rhs.m_x; - m_y = rhs.m_y; - m_row = rhs.m_row; - m_col = rhs.m_col; - m_data = rhs.m_data; - m_oldData = rhs.m_oldData; - m_offset = rhs.m_offset; - m_tile = rhs.m_tile; - m_oldTile = rhs.m_oldTile; - m_writable = rhs.m_writable; - if (m_tile) - m_tile->addReader(); - } - return *this; -} - -TQ_UINT8 * KisTiledIterator::rawData() const -{ - return m_data + m_offset; -} - - -const TQ_UINT8 * KisTiledIterator::oldRawData() const -{ -#ifdef DEBUG - // Warn if we're misusing oldRawData(). If there's no memento, oldRawData is the same - // as rawData(). - kdWarning(!m_ktm->hasCurrentMemento(), DBG_AREA_TILES) << "Accessing oldRawData() when no transaction is in progress.\n"; -#endif - return m_oldData + m_offset; -} - -void KisTiledIterator::fetchTileData(TQ_INT32 col, TQ_INT32 row) -{ - if (m_tile) - m_tile->removeReader(); - if (m_oldTile) - m_oldTile->removeReader(); - m_oldTile = 0; - - m_tile = m_ktm->getTile(col, row, m_writable); - - if (m_tile == 0) return; - //Q_ASSERT(m_tile != 0); - m_tile->addReader(); - - m_data = m_tile->data(); - if (m_data == 0) return; - - //Q_ASSERT(m_data != 0); - - // set old data but default to current value - m_oldTile = m_ktm->getOldTile(col, row, m_tile); - m_oldTile->addReader(); // Double locking in case m_oldTile==m_tile is no problem - m_oldData = m_oldTile->data(); -} diff --git a/chalk/core/tiles/kis_tilediterator.cpp b/chalk/core/tiles/kis_tilediterator.cpp new file mode 100644 index 00000000..0f3bdf00 --- /dev/null +++ b/chalk/core/tiles/kis_tilediterator.cpp @@ -0,0 +1,131 @@ +/* + * This file is part of the Chalk + * + * Copyright (c) 2004 Casper Boemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include + +#include "kis_tile_global.h" +#include "kis_tilediterator.h" + +KisTiledIterator::KisTiledIterator( KisTiledDataManager *ndevice) +{ + Q_ASSERT(ndevice != 0); + m_ktm = ndevice; + m_x = 0; + m_y = 0; + m_row = 0; + m_col = 0; + m_pixelSize = m_ktm->pixelSize(); + m_tile = 0; + m_oldTile = 0; +} + +KisTiledIterator::~KisTiledIterator( ) +{ + if (m_tile) + m_tile->removeReader(); + if (m_oldTile) + m_oldTile->removeReader(); +} + +KisTiledIterator::KisTiledIterator(const KisTiledIterator& rhs) + : TDEShared() +{ + if (this != &rhs) { + m_ktm = rhs.m_ktm; + m_pixelSize = rhs.m_pixelSize; + m_x = rhs.m_x; + m_y = rhs.m_y; + m_row = rhs.m_row; + m_col = rhs.m_col; + m_data = rhs.m_data; + m_oldData = rhs.m_oldData; + m_offset = rhs.m_offset; + m_tile = rhs.m_tile; + m_oldTile = rhs.m_oldTile; + m_writable = rhs.m_writable; + if (m_tile) + m_tile->addReader(); + } +} + +KisTiledIterator& KisTiledIterator::operator=(const KisTiledIterator& rhs) +{ + if (this != &rhs) { + if (m_tile) + m_tile->removeReader(); + if (m_oldTile) + m_oldTile->removeReader(); + m_ktm = rhs.m_ktm; + m_pixelSize = rhs.m_pixelSize; + m_x = rhs.m_x; + m_y = rhs.m_y; + m_row = rhs.m_row; + m_col = rhs.m_col; + m_data = rhs.m_data; + m_oldData = rhs.m_oldData; + m_offset = rhs.m_offset; + m_tile = rhs.m_tile; + m_oldTile = rhs.m_oldTile; + m_writable = rhs.m_writable; + if (m_tile) + m_tile->addReader(); + } + return *this; +} + +TQ_UINT8 * KisTiledIterator::rawData() const +{ + return m_data + m_offset; +} + + +const TQ_UINT8 * KisTiledIterator::oldRawData() const +{ +#ifdef DEBUG + // Warn if we're misusing oldRawData(). If there's no memento, oldRawData is the same + // as rawData(). + kdWarning(!m_ktm->hasCurrentMemento(), DBG_AREA_TILES) << "Accessing oldRawData() when no transaction is in progress.\n"; +#endif + return m_oldData + m_offset; +} + +void KisTiledIterator::fetchTileData(TQ_INT32 col, TQ_INT32 row) +{ + if (m_tile) + m_tile->removeReader(); + if (m_oldTile) + m_oldTile->removeReader(); + m_oldTile = 0; + + m_tile = m_ktm->getTile(col, row, m_writable); + + if (m_tile == 0) return; + //Q_ASSERT(m_tile != 0); + m_tile->addReader(); + + m_data = m_tile->data(); + if (m_data == 0) return; + + //Q_ASSERT(m_data != 0); + + // set old data but default to current value + m_oldTile = m_ktm->getOldTile(col, row, m_tile); + m_oldTile->addReader(); // Double locking in case m_oldTile==m_tile is no problem + m_oldData = m_oldTile->data(); +} diff --git a/chalk/core/tiles/kis_tiledrectiterator.cc b/chalk/core/tiles/kis_tiledrectiterator.cc deleted file mode 100644 index 8f0f7ed1..00000000 --- a/chalk/core/tiles/kis_tiledrectiterator.cc +++ /dev/null @@ -1,242 +0,0 @@ -/* - * This file is part of the KDE project - * - * Copyright (c) 2004 Casper Boemann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -#include - -#include "kis_tile_global.h" -#include "kis_tilediterator.h" - -KisTiledRectIterator::KisTiledRectIterator( KisTiledDataManager *ndevice, TQ_INT32 nleft, - TQ_INT32 ntop, TQ_INT32 nw, TQ_INT32 nh, bool writable) : - KisTiledIterator(ndevice), - m_left(nleft), - m_top(ntop), - m_w(nw), - m_h(nh) -{ - - Q_ASSERT(ndevice != 0); - - m_writable = writable; - m_x = nleft; - m_y = ntop; - m_beyondEnd = (m_w == 0) || (m_h == 0); - - // Find tile row,col matching x,y - m_topRow = yToRow(m_y); - m_bottomRow = yToRow(m_y + m_h - 1); - m_leftCol = xToCol(m_x); - m_rightCol = xToCol(m_x + m_w - 1); - m_row = m_topRow; - m_col = m_leftCol; - - // calc limits within the tile - m_topInTile = m_top - m_topRow * KisTile::HEIGHT; - - if(m_row == m_bottomRow) - m_bottomInTile = m_top + m_h - 1 - m_bottomRow * KisTile::HEIGHT; - else - m_bottomInTile = KisTile::HEIGHT - 1; - - m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; - - if(m_col == m_rightCol) - m_rightInTile = m_left + m_w - 1 - m_rightCol * KisTile::WIDTH; - else - m_rightInTile = KisTile::WIDTH - 1; - - m_xInTile = m_leftInTile; - m_yInTile = m_topInTile; - - if( ! m_beyondEnd) - fetchTileData(m_col, m_row); - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); -} - -KisTiledRectIterator::KisTiledRectIterator(const KisTiledRectIterator& rhs) - : KisTiledIterator(rhs) -{ - if (this != &rhs) { - m_left = rhs.m_left; - m_top = rhs.m_top; - m_w = rhs.m_w; - m_h = rhs.m_h; - m_topRow = rhs.m_topRow; - m_bottomRow = rhs.m_bottomRow; - m_leftCol = rhs.m_leftCol; - m_rightCol = rhs.m_rightCol; - m_xInTile = rhs.m_xInTile; - m_yInTile = rhs.m_yInTile; - m_leftInTile = rhs.m_leftInTile; - m_rightInTile = rhs.m_rightInTile; - m_topInTile = rhs.m_topInTile; - m_bottomInTile = rhs.m_bottomInTile; - m_beyondEnd = rhs.m_beyondEnd; - } -} - -KisTiledRectIterator& KisTiledRectIterator::operator=(const KisTiledRectIterator& rhs) -{ - if (this != &rhs) { - KisTiledIterator::operator=(rhs); - m_left = rhs.m_left; - m_top = rhs.m_top; - m_w = rhs.m_w; - m_h = rhs.m_h; - m_topRow = rhs.m_topRow; - m_bottomRow = rhs.m_bottomRow; - m_leftCol = rhs.m_leftCol; - m_rightCol = rhs.m_rightCol; - m_xInTile = rhs.m_xInTile; - m_yInTile = rhs.m_yInTile; - m_leftInTile = rhs.m_leftInTile; - m_rightInTile = rhs.m_rightInTile; - m_topInTile = rhs.m_topInTile; - m_bottomInTile = rhs.m_bottomInTile; - m_beyondEnd = rhs.m_beyondEnd; - } - return *this; -} - -KisTiledRectIterator::~KisTiledRectIterator( ) -{ -} - -TQ_INT32 KisTiledRectIterator::nConseqPixels() const -{ - if(m_leftInTile || (m_rightInTile != KisTile::WIDTH - 1)) - return m_rightInTile - m_xInTile + 1; - else - return KisTile::WIDTH * (m_bottomInTile - m_yInTile + 1) - m_xInTile; -} - -KisTiledRectIterator & KisTiledRectIterator::operator+=(int n) -{ - int remainInTile; - - remainInTile= (m_bottomInTile - m_yInTile) * (m_rightInTile - m_leftInTile + 1); - remainInTile += m_rightInTile - m_xInTile + 1; - - // This while loop may not bet the fastest, but usually it's not entered more than once. - while(n >= remainInTile) - { - n -= remainInTile; - nextTile(); - if(m_beyondEnd) - return *this; - m_yInTile = m_topInTile; - m_xInTile = m_leftInTile; - remainInTile= (m_bottomInTile - m_yInTile) * (m_rightInTile - m_leftInTile + 1); - remainInTile += m_rightInTile - m_xInTile + 1; - } - - int lWidth = m_rightInTile - m_leftInTile + 1; - while(n >= lWidth) - { - n -= lWidth; - m_yInTile++; - } - m_xInTile += n; - m_x = m_col * KisTile::WIDTH + m_xInTile; - m_y = m_row * KisTile::HEIGHT + m_yInTile; - fetchTileData(m_col, m_row); - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); - - return *this; -} - - -KisTiledRectIterator & KisTiledRectIterator::operator ++ () -{ - // advance through rect completing each tile before moving on - // as per excellent suggestion by Cyrille, avoiding excessive tile switching - if(m_xInTile >= m_rightInTile) - { - if (m_yInTile >= m_bottomInTile) - { - nextTile(); - if(m_beyondEnd) - return *this; - m_yInTile = m_topInTile; - m_x = m_col * KisTile::WIDTH + m_leftInTile; - m_y = m_row * KisTile::HEIGHT + m_topInTile; - fetchTileData(m_col, m_row); - } - else - { - m_x -= m_rightInTile - m_leftInTile; - m_y++; - m_yInTile++; - } - m_xInTile =m_leftInTile; - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); - } - else - { - m_x++; - m_xInTile++; - m_offset += m_pixelSize; - } - return *this; -} - -void KisTiledRectIterator::nextTile() -{ - if(m_col >= m_rightCol) - { - // needs to switch row - if(m_row >= m_bottomRow) - m_beyondEnd = true; - else - { - m_col = m_leftCol; - m_row++; - // The row has now changed, so recalc vertical limits - if(m_row == m_topRow) - m_topInTile = m_top - m_topRow * KisTile::HEIGHT; - else - m_topInTile = 0; - - if(m_row == m_bottomRow) - m_bottomInTile = m_top + m_h - 1 - m_bottomRow * KisTile::HEIGHT; - else - m_bottomInTile = KisTile::HEIGHT - 1; - } - } - else - m_col++; - - // No matter what the column has now changed, so recalc horizontal limits - if(m_col == m_leftCol) - m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; - else - m_leftInTile = 0; - - if(m_col == m_rightCol) - m_rightInTile = m_left + m_w - 1 - m_rightCol * KisTile::WIDTH; - else - m_rightInTile = KisTile::WIDTH - 1; -} - -/* -KisTiledRectIterator & KisTiledRectIterator::operator -- () -{ - return *this; -} -*/ diff --git a/chalk/core/tiles/kis_tiledrectiterator.cpp b/chalk/core/tiles/kis_tiledrectiterator.cpp new file mode 100644 index 00000000..8f0f7ed1 --- /dev/null +++ b/chalk/core/tiles/kis_tiledrectiterator.cpp @@ -0,0 +1,242 @@ +/* + * This file is part of the KDE project + * + * Copyright (c) 2004 Casper Boemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include + +#include "kis_tile_global.h" +#include "kis_tilediterator.h" + +KisTiledRectIterator::KisTiledRectIterator( KisTiledDataManager *ndevice, TQ_INT32 nleft, + TQ_INT32 ntop, TQ_INT32 nw, TQ_INT32 nh, bool writable) : + KisTiledIterator(ndevice), + m_left(nleft), + m_top(ntop), + m_w(nw), + m_h(nh) +{ + + Q_ASSERT(ndevice != 0); + + m_writable = writable; + m_x = nleft; + m_y = ntop; + m_beyondEnd = (m_w == 0) || (m_h == 0); + + // Find tile row,col matching x,y + m_topRow = yToRow(m_y); + m_bottomRow = yToRow(m_y + m_h - 1); + m_leftCol = xToCol(m_x); + m_rightCol = xToCol(m_x + m_w - 1); + m_row = m_topRow; + m_col = m_leftCol; + + // calc limits within the tile + m_topInTile = m_top - m_topRow * KisTile::HEIGHT; + + if(m_row == m_bottomRow) + m_bottomInTile = m_top + m_h - 1 - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + + m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; + + if(m_col == m_rightCol) + m_rightInTile = m_left + m_w - 1 - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; + + m_xInTile = m_leftInTile; + m_yInTile = m_topInTile; + + if( ! m_beyondEnd) + fetchTileData(m_col, m_row); + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} + +KisTiledRectIterator::KisTiledRectIterator(const KisTiledRectIterator& rhs) + : KisTiledIterator(rhs) +{ + if (this != &rhs) { + m_left = rhs.m_left; + m_top = rhs.m_top; + m_w = rhs.m_w; + m_h = rhs.m_h; + m_topRow = rhs.m_topRow; + m_bottomRow = rhs.m_bottomRow; + m_leftCol = rhs.m_leftCol; + m_rightCol = rhs.m_rightCol; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_leftInTile = rhs.m_leftInTile; + m_rightInTile = rhs.m_rightInTile; + m_topInTile = rhs.m_topInTile; + m_bottomInTile = rhs.m_bottomInTile; + m_beyondEnd = rhs.m_beyondEnd; + } +} + +KisTiledRectIterator& KisTiledRectIterator::operator=(const KisTiledRectIterator& rhs) +{ + if (this != &rhs) { + KisTiledIterator::operator=(rhs); + m_left = rhs.m_left; + m_top = rhs.m_top; + m_w = rhs.m_w; + m_h = rhs.m_h; + m_topRow = rhs.m_topRow; + m_bottomRow = rhs.m_bottomRow; + m_leftCol = rhs.m_leftCol; + m_rightCol = rhs.m_rightCol; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_leftInTile = rhs.m_leftInTile; + m_rightInTile = rhs.m_rightInTile; + m_topInTile = rhs.m_topInTile; + m_bottomInTile = rhs.m_bottomInTile; + m_beyondEnd = rhs.m_beyondEnd; + } + return *this; +} + +KisTiledRectIterator::~KisTiledRectIterator( ) +{ +} + +TQ_INT32 KisTiledRectIterator::nConseqPixels() const +{ + if(m_leftInTile || (m_rightInTile != KisTile::WIDTH - 1)) + return m_rightInTile - m_xInTile + 1; + else + return KisTile::WIDTH * (m_bottomInTile - m_yInTile + 1) - m_xInTile; +} + +KisTiledRectIterator & KisTiledRectIterator::operator+=(int n) +{ + int remainInTile; + + remainInTile= (m_bottomInTile - m_yInTile) * (m_rightInTile - m_leftInTile + 1); + remainInTile += m_rightInTile - m_xInTile + 1; + + // This while loop may not bet the fastest, but usually it's not entered more than once. + while(n >= remainInTile) + { + n -= remainInTile; + nextTile(); + if(m_beyondEnd) + return *this; + m_yInTile = m_topInTile; + m_xInTile = m_leftInTile; + remainInTile= (m_bottomInTile - m_yInTile) * (m_rightInTile - m_leftInTile + 1); + remainInTile += m_rightInTile - m_xInTile + 1; + } + + int lWidth = m_rightInTile - m_leftInTile + 1; + while(n >= lWidth) + { + n -= lWidth; + m_yInTile++; + } + m_xInTile += n; + m_x = m_col * KisTile::WIDTH + m_xInTile; + m_y = m_row * KisTile::HEIGHT + m_yInTile; + fetchTileData(m_col, m_row); + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + + return *this; +} + + +KisTiledRectIterator & KisTiledRectIterator::operator ++ () +{ + // advance through rect completing each tile before moving on + // as per excellent suggestion by Cyrille, avoiding excessive tile switching + if(m_xInTile >= m_rightInTile) + { + if (m_yInTile >= m_bottomInTile) + { + nextTile(); + if(m_beyondEnd) + return *this; + m_yInTile = m_topInTile; + m_x = m_col * KisTile::WIDTH + m_leftInTile; + m_y = m_row * KisTile::HEIGHT + m_topInTile; + fetchTileData(m_col, m_row); + } + else + { + m_x -= m_rightInTile - m_leftInTile; + m_y++; + m_yInTile++; + } + m_xInTile =m_leftInTile; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + } + else + { + m_x++; + m_xInTile++; + m_offset += m_pixelSize; + } + return *this; +} + +void KisTiledRectIterator::nextTile() +{ + if(m_col >= m_rightCol) + { + // needs to switch row + if(m_row >= m_bottomRow) + m_beyondEnd = true; + else + { + m_col = m_leftCol; + m_row++; + // The row has now changed, so recalc vertical limits + if(m_row == m_topRow) + m_topInTile = m_top - m_topRow * KisTile::HEIGHT; + else + m_topInTile = 0; + + if(m_row == m_bottomRow) + m_bottomInTile = m_top + m_h - 1 - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + } + } + else + m_col++; + + // No matter what the column has now changed, so recalc horizontal limits + if(m_col == m_leftCol) + m_leftInTile = m_left - m_leftCol * KisTile::WIDTH; + else + m_leftInTile = 0; + + if(m_col == m_rightCol) + m_rightInTile = m_left + m_w - 1 - m_rightCol * KisTile::WIDTH; + else + m_rightInTile = KisTile::WIDTH - 1; +} + +/* +KisTiledRectIterator & KisTiledRectIterator::operator -- () +{ + return *this; +} +*/ diff --git a/chalk/core/tiles/kis_tiledvlineiterator.cc b/chalk/core/tiles/kis_tiledvlineiterator.cc deleted file mode 100644 index 0fe8514f..00000000 --- a/chalk/core/tiles/kis_tiledvlineiterator.cc +++ /dev/null @@ -1,154 +0,0 @@ -/* - * This file is part of the Chalk - * - * Copyright (c) 2004 Casper Boemann - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -#include - -#include "kis_tile_global.h" -#include "kis_tilediterator.h" - -KisTiledVLineIterator::KisTiledVLineIterator( KisTiledDataManager *ndevice, TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable) : - KisTiledIterator(ndevice), - m_bottom(y + h - 1) -{ - m_writable = writable; - m_top = y; - m_x = x; - m_y = y; - - // Find tile row,col matching x,y - m_col = xToCol(m_x); - m_topRow = yToRow(m_y); - m_bottomRow = yToRow(m_bottom); - m_row = m_topRow; - - // calc limits within the tile - m_xInTile = m_x - m_col * KisTile::WIDTH; - m_topInTile = m_y - m_topRow * KisTile::HEIGHT; - - if(m_row == m_bottomRow) - m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; - else - m_bottomInTile = KisTile::HEIGHT - 1; - - m_yInTile = m_topInTile; - - fetchTileData(m_col, m_row); - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); -} - -KisTiledVLineIterator::KisTiledVLineIterator(const KisTiledVLineIterator& rhs) - : KisTiledIterator(rhs) -{ - if (this != &rhs) { - m_top = rhs.m_top; - m_bottom = rhs.m_bottom; - m_topRow = rhs.m_topRow; - m_bottomRow = rhs.m_bottomRow; - m_xInTile = rhs.m_xInTile; - m_yInTile = rhs.m_yInTile; - m_topInTile = rhs.m_topInTile; - m_bottomInTile = rhs.m_bottomInTile; - } -} - -KisTiledVLineIterator& KisTiledVLineIterator::operator=(const KisTiledVLineIterator& rhs) -{ - if (this != &rhs) { - KisTiledIterator::operator=(rhs); - - m_top = rhs.m_top; - m_bottom = rhs.m_bottom; - m_topRow = rhs.m_topRow; - m_bottomRow = rhs.m_bottomRow; - m_xInTile = rhs.m_xInTile; - m_yInTile = rhs.m_yInTile; - m_topInTile = rhs.m_topInTile; - m_bottomInTile = rhs.m_bottomInTile; - } - return *this; -} - -KisTiledVLineIterator::~KisTiledVLineIterator( ) -{ -} - -KisTiledVLineIterator & KisTiledVLineIterator::operator ++ () -{ - if(m_yInTile >= m_bottomInTile) - { - nextTile(); - fetchTileData(m_col, m_row); - m_yInTile =m_topInTile; - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); - } - else - { - m_yInTile++; - m_offset += m_pixelSize * KisTile::WIDTH; - } - m_y++; - - return *this; -} - -void KisTiledVLineIterator::nextTile() -{ - if(m_row < m_bottomRow) - { - m_row++; - m_topInTile = 0; - - if(m_row == m_bottomRow) - m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; - else - m_bottomInTile = KisTile::HEIGHT - 1; - } -} - -void KisTiledVLineIterator::nextCol() -{ - m_x++; - m_xInTile++; - m_y = m_top; - m_topInTile = m_y - m_topRow * KisTile::HEIGHT; - m_yInTile = m_topInTile; - if( m_xInTile >= KisTile::WIDTH ) - { // Need a new row - m_xInTile = 0; - m_col++; - m_row = m_topRow; - fetchTileData(m_col, m_row); - } else if( m_topRow != m_row ) { - m_row = m_topRow; - fetchTileData(m_col, m_row); - } - if(m_row == m_bottomRow) - m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; - else - m_bottomInTile = KisTile::HEIGHT - 1; - - m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); -} - -/* -KisTiledVLineIterator & KisTiledVLineIterator::operator -- () -{ - return *this; -} -*/ diff --git a/chalk/core/tiles/kis_tiledvlineiterator.cpp b/chalk/core/tiles/kis_tiledvlineiterator.cpp new file mode 100644 index 00000000..0fe8514f --- /dev/null +++ b/chalk/core/tiles/kis_tiledvlineiterator.cpp @@ -0,0 +1,154 @@ +/* + * This file is part of the Chalk + * + * Copyright (c) 2004 Casper Boemann + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include + +#include "kis_tile_global.h" +#include "kis_tilediterator.h" + +KisTiledVLineIterator::KisTiledVLineIterator( KisTiledDataManager *ndevice, TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable) : + KisTiledIterator(ndevice), + m_bottom(y + h - 1) +{ + m_writable = writable; + m_top = y; + m_x = x; + m_y = y; + + // Find tile row,col matching x,y + m_col = xToCol(m_x); + m_topRow = yToRow(m_y); + m_bottomRow = yToRow(m_bottom); + m_row = m_topRow; + + // calc limits within the tile + m_xInTile = m_x - m_col * KisTile::WIDTH; + m_topInTile = m_y - m_topRow * KisTile::HEIGHT; + + if(m_row == m_bottomRow) + m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + + m_yInTile = m_topInTile; + + fetchTileData(m_col, m_row); + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} + +KisTiledVLineIterator::KisTiledVLineIterator(const KisTiledVLineIterator& rhs) + : KisTiledIterator(rhs) +{ + if (this != &rhs) { + m_top = rhs.m_top; + m_bottom = rhs.m_bottom; + m_topRow = rhs.m_topRow; + m_bottomRow = rhs.m_bottomRow; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_topInTile = rhs.m_topInTile; + m_bottomInTile = rhs.m_bottomInTile; + } +} + +KisTiledVLineIterator& KisTiledVLineIterator::operator=(const KisTiledVLineIterator& rhs) +{ + if (this != &rhs) { + KisTiledIterator::operator=(rhs); + + m_top = rhs.m_top; + m_bottom = rhs.m_bottom; + m_topRow = rhs.m_topRow; + m_bottomRow = rhs.m_bottomRow; + m_xInTile = rhs.m_xInTile; + m_yInTile = rhs.m_yInTile; + m_topInTile = rhs.m_topInTile; + m_bottomInTile = rhs.m_bottomInTile; + } + return *this; +} + +KisTiledVLineIterator::~KisTiledVLineIterator( ) +{ +} + +KisTiledVLineIterator & KisTiledVLineIterator::operator ++ () +{ + if(m_yInTile >= m_bottomInTile) + { + nextTile(); + fetchTileData(m_col, m_row); + m_yInTile =m_topInTile; + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); + } + else + { + m_yInTile++; + m_offset += m_pixelSize * KisTile::WIDTH; + } + m_y++; + + return *this; +} + +void KisTiledVLineIterator::nextTile() +{ + if(m_row < m_bottomRow) + { + m_row++; + m_topInTile = 0; + + if(m_row == m_bottomRow) + m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + } +} + +void KisTiledVLineIterator::nextCol() +{ + m_x++; + m_xInTile++; + m_y = m_top; + m_topInTile = m_y - m_topRow * KisTile::HEIGHT; + m_yInTile = m_topInTile; + if( m_xInTile >= KisTile::WIDTH ) + { // Need a new row + m_xInTile = 0; + m_col++; + m_row = m_topRow; + fetchTileData(m_col, m_row); + } else if( m_topRow != m_row ) { + m_row = m_topRow; + fetchTileData(m_col, m_row); + } + if(m_row == m_bottomRow) + m_bottomInTile = m_bottom - m_bottomRow * KisTile::HEIGHT; + else + m_bottomInTile = KisTile::HEIGHT - 1; + + m_offset = m_pixelSize * (m_yInTile * KisTile::WIDTH + m_xInTile); +} + +/* +KisTiledVLineIterator & KisTiledVLineIterator::operator -- () +{ + return *this; +} +*/ diff --git a/chalk/core/tiles/kis_tilemanager.cc b/chalk/core/tiles/kis_tilemanager.cc deleted file mode 100644 index aa8a207c..00000000 --- a/chalk/core/tiles/kis_tilemanager.cc +++ /dev/null @@ -1,578 +0,0 @@ -/* - * Copyright (c) 2005-2006 Bart Coppens - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include - -#include "kis_tileddatamanager.h" -#include "kis_tile.h" -#include "kis_tilemanager.h" - -// Note: the cache file doesn't get deleted when we crash and so :( - -KisTileManager* KisTileManager::m_singleton = 0; - -static KStaticDeleter staticDeleter; - -KisTileManager::KisTileManager() { - - Q_ASSERT(KisTileManager::m_singleton == 0); - KisTileManager::m_singleton = this; - m_bytesInMem = 0; - m_bytesTotal = 0; - m_swapForbidden = false; - - // Hardcoded (at the moment only?): 4 pools of 1000 tiles each - m_tilesPerPool = 1000; - - m_pools = new TQ_UINT8*[4]; - m_poolPixelSizes = new TQ_INT32[4]; - m_poolFreeList = new PoolFreeList[4]; - for (int i = 0; i < 4; i++) { - m_pools[i] = 0; - m_poolPixelSizes[i] = 0; - m_poolFreeList[i] = PoolFreeList(); - } - m_currentInMem = 0; - - TDEConfig * cfg = TDEGlobal::config(); - cfg->setGroup(""); - m_maxInMem = cfg->readNumEntry("maxtilesinmem", 4000); - m_swappiness = cfg->readNumEntry("swappiness", 100); - - m_tileSize = KisTile::WIDTH * KisTile::HEIGHT; - m_freeLists.resize(8); - - counter = 0; - - m_poolMutex = new TQMutex(true); - m_swapMutex = new TQMutex(true); -} - -KisTileManager::~KisTileManager() { - if (!m_freeLists.empty()) { // See if there are any nonempty freelists - FreeListList::iterator listsIt = m_freeLists.begin(); - FreeListList::iterator listsEnd = m_freeLists.end(); - - while(listsIt != listsEnd) { - if ( ! (*listsIt).empty() ) { - FreeList::iterator it = (*listsIt).begin(); - FreeList::iterator end = (*listsIt).end(); - - while (it != end) { - delete *it; - ++it; - } - (*listsIt).clear(); - } - ++listsIt; - } - m_freeLists.clear(); - } - - for (FileList::iterator it = m_files.begin(); it != m_files.end(); ++it) { - (*it).tempFile->close(); - (*it).tempFile->unlink(); - delete (*it).tempFile; - } - - delete [] m_poolPixelSizes; - delete [] m_pools; - - delete m_poolMutex; - delete m_swapMutex; -} - -KisTileManager* KisTileManager::instance() -{ - if(KisTileManager::m_singleton == 0) { - staticDeleter.setObject(KisTileManager::m_singleton, new KisTileManager()); - TQ_CHECK_PTR(KisTileManager::m_singleton); - } - return KisTileManager::m_singleton; -} - -void KisTileManager::registerTile(KisTile* tile) -{ - - m_swapMutex->lock(); - - TileInfo* info = new TileInfo(); - info->tile = tile; - info->inMem = true; - info->mmapped = false; - info->onFile = false; - info->file = 0; - info->filePos = 0; - info->size = tile->WIDTH * tile->HEIGHT * tile->m_pixelSize; - info->fsize = 0; // the size in the file - info->validNode = true; - - m_tileMap[tile] = info; - m_swappableList.push_back(info); - info->node = -- m_swappableList.end(); - - m_currentInMem++; - m_bytesTotal += info->size; - m_bytesInMem += info->size; - - doSwapping(); - - if (++counter % 50 == 0) - printInfo(); - - m_swapMutex->unlock(); -} - -void KisTileManager::deregisterTile(KisTile* tile) { - - m_swapMutex->lock(); - - if (!m_tileMap.contains(tile)) { - m_swapMutex->unlock(); - return; - } - // Q_ASSERT(m_tileMap.contains(tile)); - - TileInfo* info = m_tileMap[tile]; - - if (info->onFile) { // It was once mmapped - // To freelist - FreeInfo* freeInfo = new FreeInfo(); - freeInfo->file = info->file; - freeInfo->filePos = info->filePos; - freeInfo->size = info->fsize; - uint pixelSize = (info->size / m_tileSize); - - // It is still mmapped? - if (info->mmapped) { - // munmap it - munmap(info->tile->m_data, info->size); - m_bytesInMem -= info->size; - m_currentInMem--; - } - - if (m_freeLists.capacity() <= pixelSize) - m_freeLists.resize(pixelSize + 1); - m_freeLists[pixelSize].push_back(freeInfo); - - // the KisTile will attempt to delete its data. This is of course silly when - // it was mmapped. So change the m_data to NULL, which is safe to delete - tile->m_data = 0; - } else { - m_bytesInMem -= info->size; - m_currentInMem--; - } - - if (info->validNode) { - m_swappableList.erase(info->node); - info->validNode = false; - } - - m_bytesTotal -= info->size; - - delete info; - m_tileMap.erase(tile); - - doSwapping(); - - m_swapMutex->unlock(); -} - -void KisTileManager::ensureTileLoaded(const KisTile* tile) -{ - - m_swapMutex->lock(); - - TileInfo* info = m_tileMap[tile]; - if (info->validNode) { - m_swappableList.erase(info->node); - info->validNode = false; - } - - if (!info->inMem) { - fromSwap(info); - } - - m_swapMutex->unlock(); -} - -void KisTileManager::maySwapTile(const KisTile* tile) -{ - - m_swapMutex->lock(); - - TileInfo* info = m_tileMap[tile]; - m_swappableList.push_back(info); - info->validNode = true; - info->node = -- m_swappableList.end(); - - doSwapping(); - - m_swapMutex->unlock(); -} - -void KisTileManager::fromSwap(TileInfo* info) -{ - m_swapMutex->lock(); - - if (info->inMem) { - m_swapMutex->unlock(); - return; - } - - doSwapping(); - - Q_ASSERT(info->onFile); - Q_ASSERT(info->file); - Q_ASSERT(!info->mmapped); - - if (!chalkMmap(info->tile->m_data, 0, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, - info->file->handle(), info->filePos)) { - kdWarning() << "fromSwap failed!" << endl; - m_swapMutex->unlock(); - return; - } - - info->inMem = true; - info->mmapped = true; - - m_currentInMem++; - m_bytesInMem += info->size; - - m_swapMutex->unlock(); -} - -void KisTileManager::toSwap(TileInfo* info) { - m_swapMutex->lock(); - - //Q_ASSERT(info->inMem); - if (!info || !info->inMem) { - m_swapMutex->unlock(); - return; - } - - KisTile *tile = info->tile; - - if (!info->onFile) { - // This tile is not yet in the file. Save it there - uint pixelSize = (info->size / m_tileSize); - bool foundFree = false; - - if (m_freeLists.capacity() > pixelSize) { - if (!m_freeLists[pixelSize].empty()) { - // found one - FreeList::iterator it = m_freeLists[pixelSize].begin(); - - info->file = (*it)->file; - info->filePos = (*it)->filePos; - info->fsize = (*it)->size; - - delete *it; - m_freeLists[pixelSize].erase(it); - - foundFree = true; - } - } - - if (!foundFree) { // No position found or free, create a new - long pagesize = sysconf(_SC_PAGESIZE); - TempFile* tfile = 0; - if (m_files.empty() || m_files.back().fileSize >= MaxSwapFileSize) { - m_files.push_back(TempFile()); - tfile = &(m_files.back()); - tfile->tempFile = new KTempFile(); - tfile->fileSize = 0; - } else { - tfile = &(m_files.back()); - } - off_t newsize = tfile->fileSize + info->size; - newsize = newsize + newsize % pagesize; - - if (ftruncate(tfile->tempFile->handle(), newsize)) { - // XXX make these maybe i18n()able and in an error box, but then through - // some kind of proxy such that we don't pollute this with GUI code - kdWarning(DBG_AREA_TILES) << "Resizing the temporary swapfile failed!" << endl; - // Be somewhat pollite and try to figure out why it failed - switch (errno) { - case EIO: kdWarning(DBG_AREA_TILES) << "Error was E IO, " - << "possible reason is a disk error!" << endl; break; - case EINVAL: kdWarning(DBG_AREA_TILES) << "Error was E INVAL, " - << "possible reason is that you are using more memory than " - << "the filesystem or disk can handle" << endl; break; - default: kdWarning(DBG_AREA_TILES) << "Errno was: " << errno << endl; - } - kdWarning(DBG_AREA_TILES) << "The swapfile is: " << tfile->tempFile->name() << endl; - kdWarning(DBG_AREA_TILES) << "Will try to avoid using the swap any further" << endl; - - kdDebug(DBG_AREA_TILES) << "Failed ftruncate info: " - << "tried adding " << info->size << " bytes " - << "(rounded to pagesize: " << newsize << ") " - << "from a " << tfile->fileSize << " bytes file" << endl; - printInfo(); - - m_swapForbidden = true; - m_swapMutex->unlock(); - return; - } - - info->file = tfile->tempFile; - info->fsize = info->size; - info->filePos = tfile->fileSize; - tfile->fileSize = newsize; - } - - //memcpy(data, tile->m_data, info->size); - TQFile* file = info->file->file(); - if(!file) { - kdWarning() << "Opening the file as TQFile failed" << endl; - m_swapForbidden = true; - m_swapMutex->unlock(); - return; - } - - int fd = file->handle(); - TQ_UINT8* data = 0; - if (!chalkMmap(data, 0, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, - fd, info->filePos)) { - kdWarning() << "Initial mmap failed" << endl; - m_swapForbidden = true; - m_swapMutex->unlock(); - return; - } - - memcpy(data, info->tile->m_data, info->size); - munmap(data, info->size); - - m_poolMutex->lock(); - if (isPoolTile(tile->m_data, tile->m_pixelSize)) - reclaimTileToPool(tile->m_data, tile->m_pixelSize); - else - delete[] tile->m_data; - m_poolMutex->unlock(); - - tile->m_data = 0; - } else { - //madvise(info->tile->m_data, info->fsize, MADV_DONTNEED); - Q_ASSERT(info->mmapped); - - // munmap it - munmap(tile->m_data, info->size); - tile->m_data = 0; - } - - info->inMem = false; - info->mmapped = false; - info->onFile = true; - - m_currentInMem--; - m_bytesInMem -= info->size; - - m_swapMutex->unlock(); -} - -void KisTileManager::doSwapping() -{ - m_swapMutex->lock(); - - if (m_swapForbidden || m_currentInMem <= m_maxInMem) { - m_swapMutex->unlock(); - return; - } - -#if 1 // enable this to enable swapping - - TQ_UINT32 count = TQMIN(m_swappableList.size(), m_swappiness); - - for (TQ_UINT32 i = 0; i < count && !m_swapForbidden; i++) { - toSwap(m_swappableList.front()); - m_swappableList.front()->validNode = false; - m_swappableList.pop_front(); - } - -#endif - - m_swapMutex->unlock(); -} - -void KisTileManager::printInfo() -{ - kdDebug(DBG_AREA_TILES) << m_bytesInMem << " out of " << m_bytesTotal << " bytes in memory\n"; - kdDebug(DBG_AREA_TILES) << m_currentInMem << " out of " << m_tileMap.size() << " tiles in memory\n"; - kdDebug(DBG_AREA_TILES) << m_files.size() << " swap files in use" << endl; - kdDebug(DBG_AREA_TILES) << m_swappableList.size() << " elements in the swapable list\n"; - kdDebug(DBG_AREA_TILES) << "Freelists information\n"; - for (uint i = 0; i < m_freeLists.capacity(); i++) { - if ( ! m_freeLists[i].empty() ) { - kdDebug(DBG_AREA_TILES) << m_freeLists[i].size() - << " elements in the freelist for pixelsize " << i << "\n"; - } - } - kdDebug(DBG_AREA_TILES) << "Pool stats (" << m_tilesPerPool << " tiles per pool)" << endl; - for (int i = 0; i < 4; i++) { - if (m_pools[i]) { - kdDebug(DBG_AREA_TILES) << "Pool " << i << ": Freelist count: " << m_poolFreeList[i].count() - << ", pixelSize: " << m_poolPixelSizes[i] << endl; - } - } - if (m_swapForbidden) - kdDebug(DBG_AREA_TILES) << "Something was wrong with the swap, see above for details" << endl; - kdDebug(DBG_AREA_TILES) << endl; -} - -TQ_UINT8* KisTileManager::requestTileData(TQ_INT32 pixelSize) -{ - m_swapMutex->lock(); - - TQ_UINT8* data = findTileFor(pixelSize); - if ( data ) { - m_swapMutex->unlock(); - return data; - } - m_swapMutex->unlock(); - return new TQ_UINT8[m_tileSize * pixelSize]; -} - -void KisTileManager::dontNeedTileData(TQ_UINT8* data, TQ_INT32 pixelSize) -{ - m_poolMutex->lock(); - if (isPoolTile(data, pixelSize)) { - reclaimTileToPool(data, pixelSize); - } else - delete[] data; - m_poolMutex->unlock(); -} - -TQ_UINT8* KisTileManager::findTileFor(TQ_INT32 pixelSize) -{ - m_poolMutex->lock(); - - for (int i = 0; i < 4; i++) { - if (m_poolPixelSizes[i] == pixelSize) { - if (!m_poolFreeList[i].isEmpty()) { - TQ_UINT8* data = m_poolFreeList[i].front(); - m_poolFreeList[i].pop_front(); - m_poolMutex->unlock(); - return data; - } - } - if (m_pools[i] == 0) { - // allocate new pool - m_poolPixelSizes[i] = pixelSize; - m_pools[i] = new TQ_UINT8[pixelSize * m_tileSize * m_tilesPerPool]; - // j = 1 because we return the first element, so no need to add it to the freelist - for (int j = 1; j < m_tilesPerPool; j++) - m_poolFreeList[i].append(&m_pools[i][j * pixelSize * m_tileSize]); - m_poolMutex->unlock(); - return m_pools[i]; - } - } - - m_poolMutex->unlock(); - return 0; -} - -bool KisTileManager::isPoolTile(TQ_UINT8* data, TQ_INT32 pixelSize) { - - if (data == 0) - return false; - - m_poolMutex->lock(); - for (int i = 0; i < 4; i++) { - if (m_poolPixelSizes[i] == pixelSize) { - bool b = data >= m_pools[i] - && data < m_pools[i] + pixelSize * m_tileSize * m_tilesPerPool; - if (b) { - m_poolMutex->unlock(); - return true; - } - } - } - m_poolMutex->unlock(); - return false; -} - -void KisTileManager::reclaimTileToPool(TQ_UINT8* data, TQ_INT32 pixelSize) { - m_poolMutex->lock(); - for (int i = 0; i < 4; i++) { - if (m_poolPixelSizes[i] == pixelSize) - if (data >= m_pools[i] && data < m_pools[i] + pixelSize * m_tileSize * m_tilesPerPool) { - m_poolFreeList[i].append(data); - } - } - m_poolMutex->unlock(); -} - -void KisTileManager::configChanged() { - TDEConfig * cfg = TDEGlobal::config(); - cfg->setGroup(""); - m_maxInMem = cfg->readNumEntry("maxtilesinmem", 4000); - m_swappiness = cfg->readNumEntry("swappiness", 100); - - m_swapMutex->lock(); - doSwapping(); - m_swapMutex->unlock(); -} - -bool KisTileManager::chalkMmap(TQ_UINT8*& result, void *start, size_t length, - int prot, int flags, int fd, off_t offset) { - result = (TQ_UINT8*) mmap(start, length, prot, flags, fd, offset); - - // Same here for warning and GUI - if (result == (TQ_UINT8*)-1) { - kdWarning(DBG_AREA_TILES) << "mmap failed: errno is " << errno << "; we're probably going to crash very soon now...\n"; - - // Try to ignore what happened and carry on, but unlikely that we'll get - // much further, since the file resizing went OK and this is memory-related... - if (errno == ENOMEM) { - kdWarning(DBG_AREA_TILES) << "mmap failed with E NOMEM! This means that " - << "either there are no more memory mappings available for Chalk, " - << "or that there is no more memory available!" << endl; - } - - kdWarning(DBG_AREA_TILES) << "Trying to continue anyway (no guarantees)" << endl; - kdWarning(DBG_AREA_TILES) << "Will try to avoid using the swap any further" << endl; - kdDebug(DBG_AREA_TILES) << "Failed mmap info: " - << "tried mapping " << length << " bytes" << endl; - if (!m_files.empty()) { - kdDebug(DBG_AREA_TILES) << "Probably to a " << m_files.back().fileSize << " bytes file" << endl; - } - printInfo(); - - // Be nice - result = 0; - - return false; - } - - return true; -} diff --git a/chalk/core/tiles/kis_tilemanager.cpp b/chalk/core/tiles/kis_tilemanager.cpp new file mode 100644 index 00000000..aa8a207c --- /dev/null +++ b/chalk/core/tiles/kis_tilemanager.cpp @@ -0,0 +1,578 @@ +/* + * Copyright (c) 2005-2006 Bart Coppens + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "kis_tileddatamanager.h" +#include "kis_tile.h" +#include "kis_tilemanager.h" + +// Note: the cache file doesn't get deleted when we crash and so :( + +KisTileManager* KisTileManager::m_singleton = 0; + +static KStaticDeleter staticDeleter; + +KisTileManager::KisTileManager() { + + Q_ASSERT(KisTileManager::m_singleton == 0); + KisTileManager::m_singleton = this; + m_bytesInMem = 0; + m_bytesTotal = 0; + m_swapForbidden = false; + + // Hardcoded (at the moment only?): 4 pools of 1000 tiles each + m_tilesPerPool = 1000; + + m_pools = new TQ_UINT8*[4]; + m_poolPixelSizes = new TQ_INT32[4]; + m_poolFreeList = new PoolFreeList[4]; + for (int i = 0; i < 4; i++) { + m_pools[i] = 0; + m_poolPixelSizes[i] = 0; + m_poolFreeList[i] = PoolFreeList(); + } + m_currentInMem = 0; + + TDEConfig * cfg = TDEGlobal::config(); + cfg->setGroup(""); + m_maxInMem = cfg->readNumEntry("maxtilesinmem", 4000); + m_swappiness = cfg->readNumEntry("swappiness", 100); + + m_tileSize = KisTile::WIDTH * KisTile::HEIGHT; + m_freeLists.resize(8); + + counter = 0; + + m_poolMutex = new TQMutex(true); + m_swapMutex = new TQMutex(true); +} + +KisTileManager::~KisTileManager() { + if (!m_freeLists.empty()) { // See if there are any nonempty freelists + FreeListList::iterator listsIt = m_freeLists.begin(); + FreeListList::iterator listsEnd = m_freeLists.end(); + + while(listsIt != listsEnd) { + if ( ! (*listsIt).empty() ) { + FreeList::iterator it = (*listsIt).begin(); + FreeList::iterator end = (*listsIt).end(); + + while (it != end) { + delete *it; + ++it; + } + (*listsIt).clear(); + } + ++listsIt; + } + m_freeLists.clear(); + } + + for (FileList::iterator it = m_files.begin(); it != m_files.end(); ++it) { + (*it).tempFile->close(); + (*it).tempFile->unlink(); + delete (*it).tempFile; + } + + delete [] m_poolPixelSizes; + delete [] m_pools; + + delete m_poolMutex; + delete m_swapMutex; +} + +KisTileManager* KisTileManager::instance() +{ + if(KisTileManager::m_singleton == 0) { + staticDeleter.setObject(KisTileManager::m_singleton, new KisTileManager()); + TQ_CHECK_PTR(KisTileManager::m_singleton); + } + return KisTileManager::m_singleton; +} + +void KisTileManager::registerTile(KisTile* tile) +{ + + m_swapMutex->lock(); + + TileInfo* info = new TileInfo(); + info->tile = tile; + info->inMem = true; + info->mmapped = false; + info->onFile = false; + info->file = 0; + info->filePos = 0; + info->size = tile->WIDTH * tile->HEIGHT * tile->m_pixelSize; + info->fsize = 0; // the size in the file + info->validNode = true; + + m_tileMap[tile] = info; + m_swappableList.push_back(info); + info->node = -- m_swappableList.end(); + + m_currentInMem++; + m_bytesTotal += info->size; + m_bytesInMem += info->size; + + doSwapping(); + + if (++counter % 50 == 0) + printInfo(); + + m_swapMutex->unlock(); +} + +void KisTileManager::deregisterTile(KisTile* tile) { + + m_swapMutex->lock(); + + if (!m_tileMap.contains(tile)) { + m_swapMutex->unlock(); + return; + } + // Q_ASSERT(m_tileMap.contains(tile)); + + TileInfo* info = m_tileMap[tile]; + + if (info->onFile) { // It was once mmapped + // To freelist + FreeInfo* freeInfo = new FreeInfo(); + freeInfo->file = info->file; + freeInfo->filePos = info->filePos; + freeInfo->size = info->fsize; + uint pixelSize = (info->size / m_tileSize); + + // It is still mmapped? + if (info->mmapped) { + // munmap it + munmap(info->tile->m_data, info->size); + m_bytesInMem -= info->size; + m_currentInMem--; + } + + if (m_freeLists.capacity() <= pixelSize) + m_freeLists.resize(pixelSize + 1); + m_freeLists[pixelSize].push_back(freeInfo); + + // the KisTile will attempt to delete its data. This is of course silly when + // it was mmapped. So change the m_data to NULL, which is safe to delete + tile->m_data = 0; + } else { + m_bytesInMem -= info->size; + m_currentInMem--; + } + + if (info->validNode) { + m_swappableList.erase(info->node); + info->validNode = false; + } + + m_bytesTotal -= info->size; + + delete info; + m_tileMap.erase(tile); + + doSwapping(); + + m_swapMutex->unlock(); +} + +void KisTileManager::ensureTileLoaded(const KisTile* tile) +{ + + m_swapMutex->lock(); + + TileInfo* info = m_tileMap[tile]; + if (info->validNode) { + m_swappableList.erase(info->node); + info->validNode = false; + } + + if (!info->inMem) { + fromSwap(info); + } + + m_swapMutex->unlock(); +} + +void KisTileManager::maySwapTile(const KisTile* tile) +{ + + m_swapMutex->lock(); + + TileInfo* info = m_tileMap[tile]; + m_swappableList.push_back(info); + info->validNode = true; + info->node = -- m_swappableList.end(); + + doSwapping(); + + m_swapMutex->unlock(); +} + +void KisTileManager::fromSwap(TileInfo* info) +{ + m_swapMutex->lock(); + + if (info->inMem) { + m_swapMutex->unlock(); + return; + } + + doSwapping(); + + Q_ASSERT(info->onFile); + Q_ASSERT(info->file); + Q_ASSERT(!info->mmapped); + + if (!chalkMmap(info->tile->m_data, 0, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, + info->file->handle(), info->filePos)) { + kdWarning() << "fromSwap failed!" << endl; + m_swapMutex->unlock(); + return; + } + + info->inMem = true; + info->mmapped = true; + + m_currentInMem++; + m_bytesInMem += info->size; + + m_swapMutex->unlock(); +} + +void KisTileManager::toSwap(TileInfo* info) { + m_swapMutex->lock(); + + //Q_ASSERT(info->inMem); + if (!info || !info->inMem) { + m_swapMutex->unlock(); + return; + } + + KisTile *tile = info->tile; + + if (!info->onFile) { + // This tile is not yet in the file. Save it there + uint pixelSize = (info->size / m_tileSize); + bool foundFree = false; + + if (m_freeLists.capacity() > pixelSize) { + if (!m_freeLists[pixelSize].empty()) { + // found one + FreeList::iterator it = m_freeLists[pixelSize].begin(); + + info->file = (*it)->file; + info->filePos = (*it)->filePos; + info->fsize = (*it)->size; + + delete *it; + m_freeLists[pixelSize].erase(it); + + foundFree = true; + } + } + + if (!foundFree) { // No position found or free, create a new + long pagesize = sysconf(_SC_PAGESIZE); + TempFile* tfile = 0; + if (m_files.empty() || m_files.back().fileSize >= MaxSwapFileSize) { + m_files.push_back(TempFile()); + tfile = &(m_files.back()); + tfile->tempFile = new KTempFile(); + tfile->fileSize = 0; + } else { + tfile = &(m_files.back()); + } + off_t newsize = tfile->fileSize + info->size; + newsize = newsize + newsize % pagesize; + + if (ftruncate(tfile->tempFile->handle(), newsize)) { + // XXX make these maybe i18n()able and in an error box, but then through + // some kind of proxy such that we don't pollute this with GUI code + kdWarning(DBG_AREA_TILES) << "Resizing the temporary swapfile failed!" << endl; + // Be somewhat pollite and try to figure out why it failed + switch (errno) { + case EIO: kdWarning(DBG_AREA_TILES) << "Error was E IO, " + << "possible reason is a disk error!" << endl; break; + case EINVAL: kdWarning(DBG_AREA_TILES) << "Error was E INVAL, " + << "possible reason is that you are using more memory than " + << "the filesystem or disk can handle" << endl; break; + default: kdWarning(DBG_AREA_TILES) << "Errno was: " << errno << endl; + } + kdWarning(DBG_AREA_TILES) << "The swapfile is: " << tfile->tempFile->name() << endl; + kdWarning(DBG_AREA_TILES) << "Will try to avoid using the swap any further" << endl; + + kdDebug(DBG_AREA_TILES) << "Failed ftruncate info: " + << "tried adding " << info->size << " bytes " + << "(rounded to pagesize: " << newsize << ") " + << "from a " << tfile->fileSize << " bytes file" << endl; + printInfo(); + + m_swapForbidden = true; + m_swapMutex->unlock(); + return; + } + + info->file = tfile->tempFile; + info->fsize = info->size; + info->filePos = tfile->fileSize; + tfile->fileSize = newsize; + } + + //memcpy(data, tile->m_data, info->size); + TQFile* file = info->file->file(); + if(!file) { + kdWarning() << "Opening the file as TQFile failed" << endl; + m_swapForbidden = true; + m_swapMutex->unlock(); + return; + } + + int fd = file->handle(); + TQ_UINT8* data = 0; + if (!chalkMmap(data, 0, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, info->filePos)) { + kdWarning() << "Initial mmap failed" << endl; + m_swapForbidden = true; + m_swapMutex->unlock(); + return; + } + + memcpy(data, info->tile->m_data, info->size); + munmap(data, info->size); + + m_poolMutex->lock(); + if (isPoolTile(tile->m_data, tile->m_pixelSize)) + reclaimTileToPool(tile->m_data, tile->m_pixelSize); + else + delete[] tile->m_data; + m_poolMutex->unlock(); + + tile->m_data = 0; + } else { + //madvise(info->tile->m_data, info->fsize, MADV_DONTNEED); + Q_ASSERT(info->mmapped); + + // munmap it + munmap(tile->m_data, info->size); + tile->m_data = 0; + } + + info->inMem = false; + info->mmapped = false; + info->onFile = true; + + m_currentInMem--; + m_bytesInMem -= info->size; + + m_swapMutex->unlock(); +} + +void KisTileManager::doSwapping() +{ + m_swapMutex->lock(); + + if (m_swapForbidden || m_currentInMem <= m_maxInMem) { + m_swapMutex->unlock(); + return; + } + +#if 1 // enable this to enable swapping + + TQ_UINT32 count = TQMIN(m_swappableList.size(), m_swappiness); + + for (TQ_UINT32 i = 0; i < count && !m_swapForbidden; i++) { + toSwap(m_swappableList.front()); + m_swappableList.front()->validNode = false; + m_swappableList.pop_front(); + } + +#endif + + m_swapMutex->unlock(); +} + +void KisTileManager::printInfo() +{ + kdDebug(DBG_AREA_TILES) << m_bytesInMem << " out of " << m_bytesTotal << " bytes in memory\n"; + kdDebug(DBG_AREA_TILES) << m_currentInMem << " out of " << m_tileMap.size() << " tiles in memory\n"; + kdDebug(DBG_AREA_TILES) << m_files.size() << " swap files in use" << endl; + kdDebug(DBG_AREA_TILES) << m_swappableList.size() << " elements in the swapable list\n"; + kdDebug(DBG_AREA_TILES) << "Freelists information\n"; + for (uint i = 0; i < m_freeLists.capacity(); i++) { + if ( ! m_freeLists[i].empty() ) { + kdDebug(DBG_AREA_TILES) << m_freeLists[i].size() + << " elements in the freelist for pixelsize " << i << "\n"; + } + } + kdDebug(DBG_AREA_TILES) << "Pool stats (" << m_tilesPerPool << " tiles per pool)" << endl; + for (int i = 0; i < 4; i++) { + if (m_pools[i]) { + kdDebug(DBG_AREA_TILES) << "Pool " << i << ": Freelist count: " << m_poolFreeList[i].count() + << ", pixelSize: " << m_poolPixelSizes[i] << endl; + } + } + if (m_swapForbidden) + kdDebug(DBG_AREA_TILES) << "Something was wrong with the swap, see above for details" << endl; + kdDebug(DBG_AREA_TILES) << endl; +} + +TQ_UINT8* KisTileManager::requestTileData(TQ_INT32 pixelSize) +{ + m_swapMutex->lock(); + + TQ_UINT8* data = findTileFor(pixelSize); + if ( data ) { + m_swapMutex->unlock(); + return data; + } + m_swapMutex->unlock(); + return new TQ_UINT8[m_tileSize * pixelSize]; +} + +void KisTileManager::dontNeedTileData(TQ_UINT8* data, TQ_INT32 pixelSize) +{ + m_poolMutex->lock(); + if (isPoolTile(data, pixelSize)) { + reclaimTileToPool(data, pixelSize); + } else + delete[] data; + m_poolMutex->unlock(); +} + +TQ_UINT8* KisTileManager::findTileFor(TQ_INT32 pixelSize) +{ + m_poolMutex->lock(); + + for (int i = 0; i < 4; i++) { + if (m_poolPixelSizes[i] == pixelSize) { + if (!m_poolFreeList[i].isEmpty()) { + TQ_UINT8* data = m_poolFreeList[i].front(); + m_poolFreeList[i].pop_front(); + m_poolMutex->unlock(); + return data; + } + } + if (m_pools[i] == 0) { + // allocate new pool + m_poolPixelSizes[i] = pixelSize; + m_pools[i] = new TQ_UINT8[pixelSize * m_tileSize * m_tilesPerPool]; + // j = 1 because we return the first element, so no need to add it to the freelist + for (int j = 1; j < m_tilesPerPool; j++) + m_poolFreeList[i].append(&m_pools[i][j * pixelSize * m_tileSize]); + m_poolMutex->unlock(); + return m_pools[i]; + } + } + + m_poolMutex->unlock(); + return 0; +} + +bool KisTileManager::isPoolTile(TQ_UINT8* data, TQ_INT32 pixelSize) { + + if (data == 0) + return false; + + m_poolMutex->lock(); + for (int i = 0; i < 4; i++) { + if (m_poolPixelSizes[i] == pixelSize) { + bool b = data >= m_pools[i] + && data < m_pools[i] + pixelSize * m_tileSize * m_tilesPerPool; + if (b) { + m_poolMutex->unlock(); + return true; + } + } + } + m_poolMutex->unlock(); + return false; +} + +void KisTileManager::reclaimTileToPool(TQ_UINT8* data, TQ_INT32 pixelSize) { + m_poolMutex->lock(); + for (int i = 0; i < 4; i++) { + if (m_poolPixelSizes[i] == pixelSize) + if (data >= m_pools[i] && data < m_pools[i] + pixelSize * m_tileSize * m_tilesPerPool) { + m_poolFreeList[i].append(data); + } + } + m_poolMutex->unlock(); +} + +void KisTileManager::configChanged() { + TDEConfig * cfg = TDEGlobal::config(); + cfg->setGroup(""); + m_maxInMem = cfg->readNumEntry("maxtilesinmem", 4000); + m_swappiness = cfg->readNumEntry("swappiness", 100); + + m_swapMutex->lock(); + doSwapping(); + m_swapMutex->unlock(); +} + +bool KisTileManager::chalkMmap(TQ_UINT8*& result, void *start, size_t length, + int prot, int flags, int fd, off_t offset) { + result = (TQ_UINT8*) mmap(start, length, prot, flags, fd, offset); + + // Same here for warning and GUI + if (result == (TQ_UINT8*)-1) { + kdWarning(DBG_AREA_TILES) << "mmap failed: errno is " << errno << "; we're probably going to crash very soon now...\n"; + + // Try to ignore what happened and carry on, but unlikely that we'll get + // much further, since the file resizing went OK and this is memory-related... + if (errno == ENOMEM) { + kdWarning(DBG_AREA_TILES) << "mmap failed with E NOMEM! This means that " + << "either there are no more memory mappings available for Chalk, " + << "or that there is no more memory available!" << endl; + } + + kdWarning(DBG_AREA_TILES) << "Trying to continue anyway (no guarantees)" << endl; + kdWarning(DBG_AREA_TILES) << "Will try to avoid using the swap any further" << endl; + kdDebug(DBG_AREA_TILES) << "Failed mmap info: " + << "tried mapping " << length << " bytes" << endl; + if (!m_files.empty()) { + kdDebug(DBG_AREA_TILES) << "Probably to a " << m_files.back().fileSize << " bytes file" << endl; + } + printInfo(); + + // Be nice + result = 0; + + return false; + } + + return true; +} -- cgit v1.2.1