diff --git a/libs/image/KisOptimizedByteArray.cpp b/libs/image/KisOptimizedByteArray.cpp index d334630751..9545549e71 100644 --- a/libs/image/KisOptimizedByteArray.cpp +++ b/libs/image/KisOptimizedByteArray.cpp @@ -1,201 +1,240 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * 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 "KisOptimizedByteArray.h" #include #include #include +namespace { + +/*****************************************************************/ +/* DefaultMemoryAllocator */ +/*****************************************************************/ struct DefaultMemoryAllocator : KisOptimizedByteArray::MemoryAllocator { KisOptimizedByteArray::MemoryChunk alloc(int size) override { return KisOptimizedByteArray::MemoryChunk(new quint8[size], size); } void free(KisOptimizedByteArray::MemoryChunk chunk) override { // chunk.first might be null delete[] chunk.first; } +}; + + +/*****************************************************************/ +/* DefaultMemoryAllocatorStore */ +/*****************************************************************/ + +struct DefaultMemoryAllocatorStore { + static DefaultMemoryAllocatorStore* instance(); + + DefaultMemoryAllocatorStore() + : m_allocator(new DefaultMemoryAllocator()) + { + } + + inline KisOptimizedByteArray::MemoryAllocatorSP allocator() const { + return m_allocator; + } - static DefaultMemoryAllocator* instance(); +private: + KisOptimizedByteArray::MemoryAllocatorSP m_allocator; }; -Q_GLOBAL_STATIC(DefaultMemoryAllocator, s_instance); +Q_GLOBAL_STATIC(DefaultMemoryAllocatorStore, s_instance); -DefaultMemoryAllocator *DefaultMemoryAllocator::instance() +DefaultMemoryAllocatorStore *DefaultMemoryAllocatorStore::instance() { return s_instance; } +} // namespace + + +/*****************************************************************/ +/* KisOptimizedByteArray::PooledMemoryAllocator */ +/*****************************************************************/ + KisOptimizedByteArray::PooledMemoryAllocator::PooledMemoryAllocator() : m_meanSize(500) { } KisOptimizedByteArray::PooledMemoryAllocator::~PooledMemoryAllocator() { Q_FOREACH (const MemoryChunk &chunk, m_chunks) { delete[] chunk.first; } } KisOptimizedByteArray::MemoryChunk KisOptimizedByteArray::PooledMemoryAllocator::alloc(int size) { MemoryChunk chunk; { QMutexLocker l(&m_mutex); if (!m_chunks.isEmpty()) { chunk = m_chunks.takeLast(); } m_meanSize(size); } if (chunk.second < size) { delete[] chunk.first; // we alloc a bit more memory for the dabs to let the chunks // be more reusable const int allocSize = 1.2 * size; chunk = KisOptimizedByteArray::MemoryChunk(new quint8[allocSize], allocSize); } return chunk; } void KisOptimizedByteArray::PooledMemoryAllocator::free(KisOptimizedByteArray::MemoryChunk chunk) { if (chunk.first) { QMutexLocker l(&m_mutex); // keep bigger chunks for ourselves and return the // smaller ones to the system if (chunk.second > 0.8 * m_meanSize.rollingMean()) { m_chunks.append(chunk); } else { delete[] chunk.first; } } } + +/*****************************************************************/ +/* KisOptimizedByteArray::Private */ +/*****************************************************************/ + struct KisOptimizedByteArray::Private : public QSharedData { Private(MemoryAllocatorSP _allocator) { - if (_allocator) { - storedAllocator = _allocator; - allocator = _allocator.data(); - } else { - allocator = DefaultMemoryAllocator::instance(); - } + storedAllocator = + _allocator ? _allocator : DefaultMemoryAllocatorStore::instance()->allocator(); + + allocator = storedAllocator.data(); } Private(const Private &rhs) : QSharedData(rhs) { allocator = rhs.allocator; storedAllocator = rhs.storedAllocator; dataSize = rhs.dataSize; if (dataSize) { data = allocator->alloc(dataSize); memcpy(data.first, rhs.data.first, dataSize); } } ~Private() { allocator->free(data); } MemoryAllocator *allocator; // stored allocator shared pointer is used only for keeping // the lifetime of the allocator until the deatch of the last // allocated chunk MemoryAllocatorSP storedAllocator; MemoryChunk data; int dataSize = 0; }; + +/*****************************************************************/ +/* KisOptimizedByteArray */ +/*****************************************************************/ + KisOptimizedByteArray::KisOptimizedByteArray(MemoryAllocatorSP allocator) : m_d(new Private(allocator)) { } KisOptimizedByteArray::KisOptimizedByteArray(const KisOptimizedByteArray &rhs) : m_d(rhs.m_d) { } KisOptimizedByteArray &KisOptimizedByteArray::operator=(const KisOptimizedByteArray &rhs) { m_d = rhs.m_d; return *this; } KisOptimizedByteArray::~KisOptimizedByteArray() { } quint8 *KisOptimizedByteArray::data() { return const_cast(m_d.data())->data.first; } const quint8 *KisOptimizedByteArray::constData() const { return const_cast(m_d.constData())->data.first; } void KisOptimizedByteArray::resize(int size) { if (size == m_d->dataSize) return; if (size > m_d->data.second) { m_d->allocator->free(m_d->data); m_d->data = m_d->allocator->alloc(size); } m_d->dataSize = size; } void KisOptimizedByteArray::fill(quint8 value, int size) { resize(size); memset(m_d->data.first, value, m_d->dataSize); } int KisOptimizedByteArray::size() const { return m_d->dataSize; } bool KisOptimizedByteArray::isEmpty() const { return m_d->dataSize; } KisOptimizedByteArray::MemoryAllocatorSP KisOptimizedByteArray::customMemoryAllocator() const { return m_d->storedAllocator; } diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/debugger.py b/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/debugger.py index 68ea0b0470..11cd704a3d 100644 --- a/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/debugger.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/debugger_scripter/debugger.py @@ -1,99 +1,103 @@ import bdb import multiprocessing import asyncio from . import debuggerformatter class Debugger(bdb.Bdb): def __init__(self, scripter, cmd): bdb.Bdb.__init__(self) self.quit = False self.debugq = multiprocessing.Queue() self.scripter = scripter self.applicationq = multiprocessing.Queue() self.filePath = self.scripter.documentcontroller.activeDocument.filePath self.application_data = {} self.exception_data = {} self.debugprocess = multiprocessing.Process(target=self._run, args=(self.filePath,)) self.currentLine = 0 bdb.Bdb.reset(self) def _run(self, filename): try: self.mainpyfile = self.canonic(filename) with open(filename, "rb") as fp: statement = "exec(compile(%r, %r, 'exec'))" % \ (fp.read(), self.mainpyfile) self.run(statement) except Exception as e: raise e def user_call(self, frame, args): name = frame.f_code.co_name or "" def user_line(self, frame): """Handler that executes with every line of code""" co = frame.f_code if self.filePath != co.co_filename: return self.currentLine = frame.f_lineno self.applicationq.put({"code": {"file": co.co_filename, "name": co.co_name, "lineNumber": str(frame.f_lineno) }, "frame": {"firstLineNumber": co.co_firstlineno, "locals": debuggerformatter.format_data(frame.f_locals), "globals": debuggerformatter.format_data(frame.f_globals) }, "trace": "line" }) if self.quit: return self.set_quit() if self.currentLine == 0: return else: cmd = self.debugq.get() if cmd == "step": return if cmd == "stop": return self.set_quit() def user_return(self, frame, value): name = frame.f_code.co_name or "" if name == '': self.applicationq.put({"quit": True}) def user_exception(self, frame, exception): self.applicationq.put({"exception": str(exception[1])}) - async def display(self): + @asyncio.coroutine + def display(self): """Coroutine for updating the UI""" while True: if self.applicationq.empty(): - await asyncio.sleep(0.3) + yield from asyncio.sleep(0.3) else: while not self.applicationq.empty(): self.application_data.update(self.applicationq.get()) self.scripter.uicontroller.repaintDebugArea() return - async def start(self): - await self.display() + @asyncio.coroutine + def start(self): + yield from self.display() - async def step(self): + @asyncio.coroutine + def step(self): self.debugq.put("step") - await self.display() + yield from self.display() - async def stop(self): + @asyncio.coroutine + def stop(self): self.debugq.put("stop") self.applicationq.put({"quit": True}) - await self.display() + yield from self.display() diff --git a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/saveaction/saveaction.py b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/saveaction/saveaction.py index 7352b1d08c..271a069ba2 100644 --- a/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/saveaction/saveaction.py +++ b/plugins/extensions/pykrita/plugin/plugins/scripter/ui_scripter/actions/saveaction/saveaction.py @@ -1,50 +1,51 @@ from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox from PyQt5.QtGui import QKeySequence from PyQt5.QtCore import Qt class SaveAction(QAction): def __init__(self, scripter, parent=None): super(SaveAction, self).__init__(parent) self.scripter = scripter self.editor = self.scripter.uicontroller.editor self.triggered.connect(self.save) self.setText('Save') self.setObjectName('save') self.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_S)) @property def parent(self): return 'File' def save(self): text = self.editor.toPlainText() fileName = '' fileExtension = '' if not self.scripter.documentcontroller.activeDocument: try: fileName = QFileDialog.getSaveFileName(self.scripter.uicontroller.mainWidget, 'Save Python File', '', 'Python File (*.py)')[0] if not fileName: return fileExtension = fileName.rsplit('.', maxsplit=1)[1] except: if not fileExtension == 'py': QMessageBox.information(self.scripter.uicontroller.mainWidget, 'Invalid File', 'Save files with .py extension') return document = self.scripter.documentcontroller.saveDocument(text, fileName) if document: self.scripter.uicontroller.setStatusBar(document.filePath) else: self.scripter.uicontroller.setStatusBar('untitled') self.editor._documentModified = False + self.scripter.uicontroller.setStatusModified() return document