//============================================================================= // // File : KvsObject_pixmap.cpp // Creation date : Fri Mar 18 14:30:48 CEST 2005 // by Tonino Imbesi(Grifisx) and Alessandro Carbone(Noldor) // // This file is part of the KVIrc IRC client distribution // Copyright (C) 2005-2008 Alessandro Carbone (elfonol at gmail dot com) // // 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 "KvsObject_pixmap.h" #include "KvsObject_widget.h" #include "KvsObject_memoryBuffer.h" #include "KviAnimatedPixmap.h" #include "kvi_debug.h" #include "KviLocale.h" #include /* @doc: pixmap @keyterms: pixmap object class @title: pixmap class @type: class @short: This class provide a pixmap. @inherits: [class]object[/class] [class]widget[/class] @description: The pixmap class is an off-screen, pixel-based paint device. @functions: !fn: $fill(, , ][, , ], [opacity], [system color:RGB or HSV]) Fills the pixmap with color and opacity.[br] !fn: $resize(,) Resizes the pixmap to w width and h height. Set [b]wh[/b] or [b]hg to [b]0[/b], to have a null pixmap. !fn: $scale(,,[]) Scales the pixmap or the animation by [b]sx[/b] horizontally and [b]sy[/b] vertically.[br] Aspect_ratio values: [pre] - IgnoreAspectRatio:the pixmap is scaled ignoring his aspect ratio. - KeepAspectRatio: pixmap is scaled to a rectangle as large as possible inside size, preserving the aspect ratio. - KeepAspectRatioByExpanding, the pixmap is scaled to a rectangle as small as possible outside size, preserving the aspect ratio. [/pre] [b]Default value is KeepAspectRatio.[/b][br][br] !fn: $load() Load a pixmap from the file . !fn: $save() Save the pixmap or the current animation frame to the file . !fn: $loadAnimation() Load an animation from the file and start it. !fn: $startAnimation() Start the animation. !fn: $stopAnimation() Stop the animation. !fn: $height() Return the height of the pixmap or of the animation. !fn: $width() Return the width of the pixmap or of the animation. !fn: $frameChangedEvent() This event is triggered by KVIrc when the current animation frame changes. @signals: !sg: $frameChanged() This signal is emitted by the default implementation of [classfnc]$frameChangedEvent[/classfnc]().[br] */ KVSO_BEGIN_REGISTERCLASS(KvsObject_pixmap, "pixmap", "object") KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, fill) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, resize) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, scale) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, load) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, loadAnimation) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, save) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, startAnimation) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, stopAnimation) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, loadFromMemoryBuffer) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, height) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, width) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, rotate) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, mirrored) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, setPixel) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, pixel) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, grabWidget) KVSO_REGISTER_HANDLER_BY_NAME(KvsObject_pixmap, frameChangedEvent) KVSO_END_REGISTERCLASS(KvsObject_pixmap) KVSO_BEGIN_CONSTRUCTOR(KvsObject_pixmap, KviKvsObject) m_pAnimatedPixmap = nullptr; m_currentType = Image; m_pPixmap = nullptr; m_pImage = nullptr; KVSO_END_CONSTRUCTOR(KvsObject_pixmap) KVSO_BEGIN_DESTRUCTOR(KvsObject_pixmap) emit aboutToDie(); if(m_pPixmap) delete m_pPixmap; if(m_pAnimatedPixmap) delete m_pAnimatedPixmap; if(m_pImage) delete m_pImage; KVSO_END_CONSTRUCTOR(KvsObject_pixmap) KVSO_CLASS_FUNCTION(pixmap, fill) { kvs_int_t iCol1, iCol2, iCol3, iOpacity; QString szColorMode, szColor; KviKvsVariant *var1, *var2, *var3; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("Color_1_Or_Colorname", KVS_PT_VARIANT, 0, var1) KVSO_PARAMETER("Color_2", KVS_PT_VARIANT, KVS_PF_OPTIONAL, var2) KVSO_PARAMETER("Colo3_3", KVS_PT_VARIANT, KVS_PF_OPTIONAL, var3) KVSO_PARAMETER("color_mode", KVS_PT_STRING, KVS_PF_OPTIONAL, szColorMode) KVSO_PARAMETER("opacity", KVS_PT_INT, KVS_PF_OPTIONAL, iOpacity) KVSO_PARAMETERS_END(c) QColor col; if(!var1->asInteger(iCol1)) { var1->asString(szColor); if(c->paramCount() < 2) iOpacity = 255; else { if(!var2->asInteger(iOpacity)) { c->warning(__tr2qs_ctx("The opacity parameter didn't evaluate to integer", "objects")); return true; } } #if (QT_VERSION < QT_VERSION_CHECK(6, 4, 0)) col.setNamedColor(szColor); #else col = QColor::fromString(szColor); #endif col.setAlpha(iOpacity); } else { if(c->paramCount() < 3) { c->error(__tr2qs_ctx("Color name or triplet RGB/HSV value required", "objects")); return true; } if(!var2->asInteger(iCol2) || !var3->asInteger(iCol3)) { c->error(__tr2qs_ctx("One of the triplet parameters didn't evaluate to an integer", "objects")); return true; } if(c->paramCount() < 5) iOpacity = 255; if(KviQString::equalCI(szColorMode, "HSV")) col.setHsv(iCol1, iCol2, iCol3, iOpacity); else col.setRgb(iCol1, iCol2, iCol3, iOpacity); } if(m_currentType == Image) { if(m_pImage) { if(!m_pPixmap) m_pPixmap = new QPixmap(); *m_pPixmap = QPixmap::fromImage(*m_pImage); delete m_pImage; m_pImage = nullptr; } else { m_pPixmap = nullptr; } } else if(m_currentType == AnimatedPixmap) { delete m_pAnimatedPixmap; m_pAnimatedPixmap = nullptr; } m_currentType = Pixmap; if(!m_pPixmap) m_pPixmap = new QPixmap(); m_pPixmap->fill(col); return true; } KVSO_CLASS_FUNCTION(pixmap, resize) { kvs_int_t iWidth, iHeight; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("width", KVS_PT_INTEGER, 0, iWidth) KVSO_PARAMETER("height", KVS_PT_INTEGER, 0, iHeight) KVSO_PARAMETERS_END(c) if(m_currentType == Image) { if(m_pImage) { delete m_pImage; m_pImage = nullptr; } } else if(m_currentType == AnimatedPixmap) { if(m_pAnimatedPixmap) { delete m_pAnimatedPixmap; m_pAnimatedPixmap = nullptr; } } else if(m_pPixmap) { delete m_pPixmap; } m_currentType = Image; m_pImage = new QImage(iWidth, iHeight, QImage::Format_ARGB32_Premultiplied); m_pImage->fill(Qt::transparent); return true; } KVSO_CLASS_FUNCTION(pixmap, setPixel) { kvs_int_t iCol1, iCol2, iCol3, iOpacity, iX, iY; QString szColorMode, szColor; KviKvsVariant *var1, *var2, *var3; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("x", KVS_PT_INTEGER, 0, iX) KVSO_PARAMETER("y", KVS_PT_INTEGER, 0, iY) KVSO_PARAMETER("Color_1_Or_Colorname", KVS_PT_VARIANT, 0, var1) KVSO_PARAMETER("Color_2", KVS_PT_VARIANT, KVS_PF_OPTIONAL, var2) KVSO_PARAMETER("Colo3_3", KVS_PT_VARIANT, KVS_PF_OPTIONAL, var3) KVSO_PARAMETER("color_mode", KVS_PT_STRING, KVS_PF_OPTIONAL, szColorMode) KVSO_PARAMETER("opacity", KVS_PT_INT, KVS_PF_OPTIONAL, iOpacity) KVSO_PARAMETERS_END(c) QColor col; if(!var1->asInteger(iCol1)) { var1->asString(szColor); if(c->paramCount() < 4) { iOpacity = 255; } else { if(!var2->asInteger(iOpacity)) { c->warning(__tr2qs_ctx("The opacity parameter didn't evaluate to integer", "objects")); return true; } } #if (QT_VERSION < QT_VERSION_CHECK(6, 4, 0)) col.setNamedColor(szColor); #else col = QColor::fromString(szColor); #endif col.setAlpha(iOpacity); } else { if(c->paramCount() < 5) { c->error(__tr2qs_ctx("Color name or triplet RGB/HSV value required", "objects")); return true; } if(!var2->asInteger(iCol2) || !var3->asInteger(iCol3)) { c->error(__tr2qs_ctx("One of the triplet parameters didn't evaluate to an integer", "objects")); return true; } if(c->paramCount() < 7) iOpacity = 255; if(KviQString::equalCI(szColorMode, "HSV")) col.setHsv(iCol1, iCol2, iCol3, iOpacity); else col.setRgb(iCol1, iCol2, iCol3, iOpacity); } if(m_currentType == AnimatedPixmap) { c->warning(__tr2qs_ctx("AnimatedPixmap not supported", "objects")); return true; } else if((m_currentType == Image && !m_pImage) || (m_currentType == Pixmap && !m_pPixmap)) { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } else if(m_currentType == Pixmap) { delete m_pImage; m_pImage = new QImage(); *m_pImage = m_pPixmap->toImage(); delete m_pPixmap; } m_currentType = Image; m_pImage->setPixel(iX, iY, col.rgba()); return true; } KVSO_CLASS_FUNCTION(pixmap, pixel) { kvs_int_t iX, iY; QString szFlags; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("x", KVS_PT_INTEGER, 0, iX) KVSO_PARAMETER("y", KVS_PT_INTEGER, 0, iY) KVSO_PARAMETER("b", KVS_PT_STRING, KVS_PF_OPTIONAL, szFlags) KVSO_PARAMETERS_END(c) if(m_currentType == AnimatedPixmap) { c->warning(__tr2qs_ctx("AnimatedPixmap not supported", "objects")); return true; } else if((m_currentType == Image && !m_pImage) || (m_currentType == Pixmap && !m_pPixmap)) { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } else if(m_currentType == Pixmap) { // qDebug("mpimage %i",m_pImage); // qDebug("pixmap %i %i",m_pPixmap->width(),m_pPixmap->height()); delete m_pImage; m_pImage = new QImage(); *m_pImage = m_pPixmap->toImage(); delete m_pPixmap; m_pPixmap = nullptr; } m_currentType = Image; QRgb rgb = m_pImage->pixel(iX, iY); QColor col(rgb); if(szFlags.isEmpty()) { c->returnValue()->setString(col.name()); return true; } if(szFlags.indexOf('a', 0, Qt::CaseInsensitive) != -1) { KviKvsArray * pArray = new KviKvsArray(); pArray->set(0, new KviKvsVariant((kvs_int_t)col.red())); pArray->set(1, new KviKvsVariant((kvs_int_t)col.green())); pArray->set(2, new KviKvsVariant((kvs_int_t)col.blue())); c->returnValue()->setArray(pArray); } else if(szFlags.indexOf('h', 0, Qt::CaseInsensitive) != -1) { KviKvsHash * pHash = new KviKvsHash(); pHash->set("red", new KviKvsVariant((kvs_int_t)col.red())); pHash->set("green", new KviKvsVariant((kvs_int_t)col.green())); pHash->set("blue", new KviKvsVariant((kvs_int_t)col.blue())); c->returnValue()->setHash(pHash); } return true; } KVSO_CLASS_FUNCTION(pixmap, rotate) { kvs_real_t dAngle; QString szAxis; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("angle", KVS_PT_DOUBLE, 0, dAngle) KVSO_PARAMETER("axis", KVS_PT_STRING, KVS_PF_OPTIONAL, szAxis) KVSO_PARAMETERS_END(c) Qt::Axis axis = Qt::ZAxis; if(!szAxis.isEmpty()) { if(KviQString::equalCI(szAxis, "XAxis")) axis = Qt::XAxis; else if(KviQString::equalCI(szAxis, "YAxis")) axis = Qt::YAxis; else if(KviQString::equalCI(szAxis, "ZAxis")) axis = Qt::ZAxis; else c->warning(__tr2qs_ctx("Unknown axis '%Q' switching to default ZAxis", "objects"), &szAxis); } if(m_currentType == Pixmap) { if(m_pPixmap) { if(!m_pImage) m_pImage = new QImage(); *m_pImage = m_pPixmap->toImage(); } else { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } } else if(m_currentType == AnimatedPixmap) { c->warning(__tr2qs_ctx("AnimatedPixmap not supported", "objects")); return true; } if(!m_pImage) { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } m_currentType = Image; QTransform transform; transform.rotate(dAngle, axis); *m_pImage = m_pImage->transformed(transform); return true; } KVSO_CLASS_FUNCTION(pixmap, mirrored) { bool bHorizontal, bVertical; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("bHorizontal", KVS_PT_BOOLEAN, 0, bHorizontal) KVSO_PARAMETER("bVertical", KVS_PT_BOOLEAN, 0, bVertical) KVSO_PARAMETERS_END(c) if(m_currentType == Pixmap) { if(m_pPixmap) { if(!m_pImage) m_pImage = new QImage(); *m_pImage = m_pPixmap->toImage(); } else { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } } else if(m_currentType == AnimatedPixmap) { c->warning(__tr2qs_ctx("AnimatedPixmap not supported", "objects")); return true; } if(!m_pImage) { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } m_currentType = Image; #if (QT_VERSION < QT_VERSION_CHECK(6, 9, 0)) *m_pImage = m_pImage->mirrored(bHorizontal, bVertical); #else Qt::Orientations orient = Qt::Orientations(); if(bHorizontal) { orient |= Qt::Horizontal; } if(bVertical) { orient |= Qt::Vertical; } *m_pImage = m_pImage->flipped(orient); #endif return true; } KVSO_CLASS_FUNCTION(pixmap, scale) { kvs_int_t iWidth, iHeight; QString szAspectRatio; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("width", KVS_PT_INTEGER, 0, iWidth) KVSO_PARAMETER("height", KVS_PT_INTEGER, 0, iHeight) KVSO_PARAMETER("aspect_ratio", KVS_PT_STRING, KVS_PF_OPTIONAL, szAspectRatio) KVSO_PARAMETERS_END(c) Qt::AspectRatioMode ratio = Qt::KeepAspectRatio; if(!szAspectRatio.isEmpty()) { if(KviQString::equalCI(szAspectRatio, "IgnoreAspectRatio")) ratio = Qt::IgnoreAspectRatio; else if(KviQString::equalCI(szAspectRatio, "KeepAspectRatio")) ratio = Qt::KeepAspectRatio; else if(KviQString::equalCI(szAspectRatio, "KeepAspectRatioByExpanding")) ratio = Qt::KeepAspectRatioByExpanding; else c->warning(__tr2qs_ctx("Unknown aspect ratio %Q - Switching to KeepAspectRatio ratio", "objects"), &szAspectRatio); } if(m_currentType == Pixmap) { if(m_pPixmap) *m_pPixmap = m_pPixmap->scaled(iWidth, iHeight, ratio, Qt::SmoothTransformation); else { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } } else if(m_currentType == AnimatedPixmap) m_pAnimatedPixmap->resize(QSize(iWidth, iHeight), ratio); else if(m_currentType == Image) { if(!m_pImage) { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } *m_pImage = m_pImage->scaled(iWidth, iHeight, ratio, Qt::SmoothTransformation); } return true; } KVSO_CLASS_FUNCTION(pixmap, loadAnimation) { QString szFile; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("file", KVS_PT_STRING, 0, szFile) KVSO_PARAMETERS_END(c) if(!QFile::exists(szFile)) { c->warning(__tr2qs_ctx("I can't find the specified file '%Q'.", "objects"), &szFile); return true; } if(m_pAnimatedPixmap) { delete m_pAnimatedPixmap; m_pAnimatedPixmap = nullptr; } if(m_pPixmap) { delete m_pPixmap; m_pPixmap = nullptr; } m_pAnimatedPixmap = new KviAnimatedPixmap(szFile); connect(m_pAnimatedPixmap, SIGNAL(frameChanged()), this, SLOT(frameChanged())); return true; } KVSO_CLASS_FUNCTION(pixmap, startAnimation) { if(m_pAnimatedPixmap) m_pAnimatedPixmap->start(); return true; } KVSO_CLASS_FUNCTION(pixmap, stopAnimation) { if(m_pAnimatedPixmap) m_pAnimatedPixmap->stop(); return true; } KVSO_CLASS_FUNCTION(pixmap, load) { QString szFile; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("file", KVS_PT_STRING, 0, szFile) KVSO_PARAMETERS_END(c) if(!QFile::exists(szFile)) { c->warning(__tr2qs_ctx("I can't find the specified file '%Q'.", "objects"), &szFile); return true; } if(!m_pImage) m_pImage = new QImage(); m_currentType = Image; m_pImage->load(szFile); return true; } KVSO_CLASS_FUNCTION(pixmap, save) { QString szFile; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("file", KVS_PT_STRING, 0, szFile) KVSO_PARAMETERS_END(c) if(m_currentType == Pixmap) { if(m_pPixmap) { m_pPixmap->save(szFile); } else { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } } else if(m_currentType == Image) { if(m_pImage) { m_pImage->save(szFile); } else { c->error(__tr2qs_ctx("The pixmap is null", "objects")); return false; } } else { m_pAnimatedPixmap->pixmap()->save(szFile); } return true; } void KvsObject_pixmap::setInternalPixmap(QPixmap * pPixmap) { if(m_currentType == Image && m_pImage) delete m_pImage; else if(m_currentType == Pixmap && m_pPixmap) delete m_pPixmap; m_currentType = Pixmap; m_pPixmap = pPixmap; } void KvsObject_pixmap::setInternalImage(QImage * pImage) { if(m_currentType == Image && m_pImage) delete m_pImage; else if(m_currentType == Pixmap && m_pPixmap) delete m_pPixmap; m_currentType = Image; m_pImage = pImage; } KVSO_CLASS_FUNCTION(pixmap, loadFromMemoryBuffer) { KviKvsObject * pObject; kvs_hobject_t hObject; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("hobject", KVS_PT_HOBJECT, 0, hObject) KVSO_PARAMETERS_END(c) pObject = KviKvsKernel::instance()->objectController()->lookupObject(hObject); if(!pObject) { c->warning(__tr2qs_ctx("Buffer parameter is not an object", "objects")); return true; } if(!pObject->inheritsClass("memorybuffer")) { c->warning(__tr2qs_ctx("Buffer parameter is not a memorybuffer object", "objects")); return true; } if(m_pAnimatedPixmap) { delete m_pAnimatedPixmap; m_pAnimatedPixmap = nullptr; } if(!m_pPixmap) m_pPixmap = new QPixmap(); m_pPixmap->loadFromData(((KvsObject_memoryBuffer *)pObject)->dataBuffer()); return true; } KVSO_CLASS_FUNCTION(pixmap, grabWidget) { CHECK_INTERNAL_POINTER(m_pPixmap) KviKvsObject * pObject; kvs_hobject_t hObject; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("widget", KVS_PT_HOBJECT, 0, hObject) KVSO_PARAMETERS_END(c) pObject = KviKvsKernel::instance()->objectController()->lookupObject(hObject); CHECK_HOBJECT_IS_WIDGET(pObject) if(m_pAnimatedPixmap) { delete m_pAnimatedPixmap; m_pAnimatedPixmap = nullptr; } if(!m_pPixmap) m_pPixmap = new QPixmap(); *m_pPixmap = ((KvsObject_widget *)pObject)->widget()->grab(); return true; } KVSO_CLASS_FUNCTION(pixmap, height) { if(m_currentType == Image && m_pImage) c->returnValue()->setInteger(m_pImage->height()); else if(m_currentType == Pixmap && m_pPixmap) c->returnValue()->setInteger(m_pPixmap->height()); else c->returnValue()->setInteger(0); return true; } KVSO_CLASS_FUNCTION(pixmap, width) { if(m_currentType == Image && m_pImage) c->returnValue()->setInteger(m_pImage->width()); else if(m_currentType == Pixmap && m_pPixmap) c->returnValue()->setInteger(m_pPixmap->width()); else c->returnValue()->setInteger(0); return true; } KVSO_CLASS_FUNCTION(pixmap, frameChangedEvent) { emitSignal("frameChanged", c); return true; } // slots void KvsObject_pixmap::frameChanged() { KviKvsVariantList * params = nullptr; callFunction(this, "frameChangedEvent", params); }