8#define GL_GLEXT_PROTOTYPES
24#include <QPainterPath>
25#include <QOpenGLPaintDevice>
30#include <QOpenGLShaderProgram>
31#include <QOpenGLVertexArrayObject>
32#include <QOpenGLBuffer>
33#include <QOpenGLFramebufferObject>
34#include <QOpenGLFramebufferObjectFormat>
44#include <config-ocio.h>
46#define NEAR_VAL -1000.0
49#ifndef GL_CLAMP_TO_EDGE
50#define GL_CLAMP_TO_EDGE 0x812F
53#define PROGRAM_VERTEX_ATTRIBUTE 0
54#define PROGRAM_TEXCOORD_ATTRIBUTE 1
123 int colsPerImage = lastImageColumn - firstImageColumn + 1;
124 int numWraps = floor(qreal(x) / imageRect.width());
125 int remainder = x - imageRect.width() * numWraps;
134 int rowsPerImage = lastImageRow - firstImageRow + 1;
135 int numWraps = floor(qreal(y) / imageRect.height());
136 int remainder = y - imageRect.height() * numWraps;
200 bool needsInternalColorManagement =
201 !displayFilter || displayFilter->useInternalColorManagement();
207 if (!initializing && needsFullRefresh) {
210 else if (!initializing) {
248 initializeOpenGLFunctions();
332 qDebug() <<
"Shader Compilation Failure: " <<
context;
334 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"),
335 i18n(
"Krita could not initialize the OpenGL canvas:\n\n%1\n\n Krita will disable OpenGL and close now.",
context),
364 QOpenGLFramebufferObjectFormat format;
373 if (!canvasImageDirtyRect.isEmpty()) {
379 if (viewportUpdateRect.isEmpty()) {
383 blitRect = scale.mapRect(QRectF(viewportUpdateRect)).toAlignedRect();
385 QOpenGLFramebufferObject::blitFramebuffer(
nullptr, blitRect,
d->
canvasFBO.data(), blitRect, GL_COLOR_BUFFER_BIT, GL_NEAREST);
386 QOpenGLFramebufferObject::bindDefault();
388 QRect fullUpdateRect = canvasImageDirtyRect | viewportUpdateRect;
389 if (fullUpdateRect.isEmpty()) {
405 QMatrix4x4 projectionMatrix;
406 projectionMatrix.setToIdentity();
409 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
413 modelMatrix.optimize();
414 modelMatrix = projectionMatrix * modelMatrix;
422 glBlendFuncSeparate(GL_ONE, GL_SRC_COLOR, GL_ONE, GL_ONE);
423 glBlendEquationSeparate(GL_FUNC_SUBTRACT, GL_FUNC_ADD);
426 if (!viewportUpdateRect.isEmpty()) {
427 const QRect deviceUpdateRect =
widgetToSurface(viewportUpdateRect).toAlignedRect();
428 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height());
429 glEnable(GL_SCISSOR_TEST);
447 for (
auto it = path.begin(); it != path.end(); ++it) {
448 const QPolygonF& polygon = *it;
454 int triangleCount = 0;
457 const bool closed = polygon.isClosed();
459 for(
int i = 1; i < polygon.count(); i++) {
460 bool adjustFirst = closed?
true: i > 1;
461 bool adjustSecond = closed?
true: i + 1 < polygon.count();
463 QPointF
p1 = polygon.at(i - 1);
464 QPointF
p2 = polygon.at(i);
465 QPointF normal =
p2 -
p1;
468 QPointF c1 =
p1 - (normal * halfWidth);
469 QPointF c2 =
p1 + (normal * halfWidth);
470 QPointF c3 =
p2 - (normal * halfWidth);
471 QPointF c4 =
p2 + (normal * halfWidth);
475 QPointF pPrev = i >= 2 ?
476 QPointF(polygon.at(i-2)) :
477 QPointF(polygon.at(qMax(polygon.count() - 2, 0)));
484 QPointF(-pPrev.y(), pPrev.x())));
489 c1 =
p1 + ((miter * -halfWidth) / dot);
490 c2 =
p1 + ((miter * halfWidth) / dot);
495 QPointF pNext = i + 1 < polygon.count()? QPointF(polygon.at(i+1))
496 : QPointF(polygon.at(qMin(polygon.count(), 1)));
504 c3 =
p2 + ((miter * -halfWidth) / dot);
505 c4 =
p2 + (miter * halfWidth) / dot;
527 glDrawArrays(GL_TRIANGLES, 0, triangleCount * 3);
531 for (
auto it = path.begin(); it != path.end(); ++it) {
532 const QPolygonF& polygon = *it;
538 const int verticesCount = polygon.count();
544 for (
int vertIndex = 0; vertIndex < verticesCount; vertIndex++) {
545 QPointF point = polygon.at(vertIndex);
560 glDrawArrays(GL_LINE_STRIP, 0, verticesCount);
569 if (!viewportUpdateRect.isEmpty()) {
570 glDisable(GL_SCISSOR_TEST);
573 glBlendEquation(GL_FUNC_ADD);
574 glBlendFunc(GL_ONE, GL_ZERO);
587 Q_UNUSED(updateRect);
598 convertedBackgroundColor.
convertTo(finalColorSpace);
605 glClearColor(channels[2], channels[1], channels[0], 1.0);
606 glClear(GL_COLOR_BUFFER_BIT);
611 Q_UNUSED(updateRect);
618 QTransform textureTransform;
619 QTransform modelTransform;
630 viewportRect = converter->
widgetToViewport(QRectF(0, 0, widgetSize.width(), widgetSize.height()));
632 viewportRect.setTop(ir.top());
633 viewportRect.setBottom(ir.bottom());
636 viewportRect.setLeft(ir.left());
637 viewportRect.setRight(ir.right());
642 if (!
canvas()->renderingLimit().isEmpty()) {
644 viewportRect &= vrect;
648 &textureTransform, &modelTransform, &textureRect, &modelRect,
d->
scrollCheckers);
654 qWarning() <<
"Could not bind checker shader";
658 QMatrix4x4 projectionMatrix;
659 projectionMatrix.setToIdentity();
662 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
665 QMatrix4x4 modelMatrix(modelTransform);
666 modelMatrix.optimize();
667 modelMatrix = projectionMatrix * modelMatrix;
670 QMatrix4x4 textureMatrix(textureTransform);
679 vertexBuf->write(0,
d->
vertices, 3 * 6 *
sizeof(
float));
686 vertexTextureBuf->bind();
687 vertexTextureBuf->write(0,
d->
texCoords, 2 * 6 *
sizeof(
float));
701 glActiveTexture(GL_TEXTURE0);
704 glDrawArrays(GL_TRIANGLES, 0, 6);
706 glBindTexture(GL_TEXTURE_2D, 0);
708 glBindBuffer(GL_ARRAY_BUFFER, 0);
719 QMatrix4x4 projectionMatrix;
720 projectionMatrix.setToIdentity();
723 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
727 modelMatrix.optimize();
728 modelMatrix = projectionMatrix * modelMatrix;
732 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
738 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height());
740 QRect wr = widgetRectInImagePixels.toAlignedRect();
746 if (!updateRect.isEmpty()) {
748 wr &= updateRectInImagePixels;
751 QPoint topLeftCorner = wr.topLeft();
752 QPoint bottomRightCorner = wr.bottomRight() + QPoint(1, 1);
755 for (
int i = topLeftCorner.x(); i <= bottomRightCorner.x(); ++i) {
756 grid.append(QVector3D(i, topLeftCorner.y(), 0));
757 grid.append(QVector3D(i, bottomRightCorner.y(), 0));
759 for (
int i = topLeftCorner.y(); i <= bottomRightCorner.y(); ++i) {
760 grid.append(QVector3D(topLeftCorner.x(), i, 0));
761 grid.append(QVector3D(bottomRightCorner.x(), i, 0));
774 glDrawArrays(GL_LINES, 0, grid.size());
792 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
800 QMatrix4x4 textureMatrix;
801 textureMatrix.setToIdentity();
804 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height());
806 if (!updateRect.isEmpty()) {
807 widgetRect &= updateRect;
814 if (!renderingLimit.isEmpty()) {
815 widgetRectInImagePixels &= renderingLimit;
818 qreal scaleX, scaleY;
825 QRect wr = widgetRectInImagePixels.toAlignedRect();
834 wr.setBottom(ir.bottom());
837 wr.setLeft(ir.left());
838 wr.setRight(ir.right());
851 const int imageColumns = maxColumn - minColumn + 1;
852 const int imageRows = maxRow - minRow + 1;
858 const int firstCloneX = qFloor(qreal(firstColumn) / imageColumns);
859 const int lastCloneX = qFloor(qreal(lastColumn) / imageColumns);
860 const int firstCloneY = qFloor(qreal(firstRow) / imageRows);
861 const int lastCloneY = qFloor(qreal(lastRow) / imageRows);
863 for (
int cloneY = firstCloneY; cloneY <= lastCloneY; cloneY++) {
864 for (
int cloneX = firstCloneX; cloneX <= lastCloneX; cloneX++) {
867 const int localLastCol = cloneX == lastCloneX ?
KisAlgebra2D::wrapValue(lastColumn, imageColumns) : imageColumns - 1;
873 localFirstRow, localLastRow,
874 scaleX, scaleY, QPoint(cloneX, cloneY));
888 QMatrix4x4 projectionMatrix;
889 projectionMatrix.setToIdentity();
892 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
896 if (!wrapAroundOffset.isNull()) {
899 const QTransform wrapAroundTranslate = QTransform::fromTranslate(ir.width() * wrapAroundOffset.x(),
900 ir.height() * wrapAroundOffset.y());
901 modelTransform = wrapAroundTranslate * modelTransform;
905 QMatrix4x4 modelMatrix(modelTransform);
906 modelMatrix.optimize();
907 modelMatrix = projectionMatrix * modelMatrix;
910 int lastTileLodPlane = -1;
912 for (
int col = firstCol; col <= lastCol; col++) {
913 for (
int row = firstRow; row <= lastRow; row++) {
919 warnUI <<
"OpenGL: Trying to paint texture tile but it has not been created yet.";
927 const int vertexRectSize = 6 * 3 *
sizeof(float);
929 glVertexAttribPointer(
PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0,
reinterpret_cast<void*
>(tileIndex * vertexRectSize));
931 const int textureRectSize = 6 * 2 *
sizeof(float);
933 glVertexAttribPointer(
PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0,
reinterpret_cast<void*
>(tileIndex * textureRectSize));
949 glActiveTexture(GL_TEXTURE0);
955 (lastTileLodPlane < 0 || lastTileLodPlane != currentLodPlane)) {
958 (GLfloat) currentLodPlane);
959 lastTileLodPlane = currentLodPlane;
962 if (currentLodPlane > 0) {
963 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
965 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
966 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
968 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
972 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
973 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
976 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
979 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
983 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
985 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
991 glDrawArrays(GL_TRIANGLES, 0, 6);
995 glBindTexture(GL_TEXTURE_2D, 0);
996 glBindBuffer(GL_ARRAY_BUFFER, 0);
1034 return QRectF(rc.x() * ratio,
1037 rc.height() * ratio);
1044 return QRectF(rc.x() / ratio,
1047 rc.height() / ratio);
1067 QRect alignedUpdateRect = updateRect;
1069 if (!updateRect.isEmpty()) {
1070 const QRect deviceUpdateRect =
widgetToSurface(updateRect).toAlignedRect();
1071 alignedUpdateRect =
surfaceToWidget(deviceUpdateRect).toAlignedRect();
1073 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height());
1074 glEnable(GL_SCISSOR_TEST);
1085 if (!updateRect.isEmpty()) {
1086 glDisable(GL_SCISSOR_TEST);
1116 if (
canvas()->proofingConfigUpdated()) {
1128 if (isOpenGLUpdateInfo) {
float value(const T *src, size_t ch)
static constexpr int NumberOfBuffers
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
const QString COMPOSITE_ERASE
void startUpdateInPatches(const QRect &imageRect)
void setProofingConfigUpdated(bool updated)
setProofingConfigUpdated This function is to set whether the proofing config is updated,...
void updateCanvas(const QRectF &rc) override
int openGLFilteringMode(bool defaultValue=false) const
void setCanvasState(const QString &state) const
qreal getPixelGridDrawingThreshold(bool defaultValue=false) const
int numMipmapLevels(bool defaultValue=false) const
void disableOpenGL() const
QColor getPixelGridColor(bool defaultValue=false) const
bool pixelGridEnabled(bool defaultValue=false) const
bool useOpenGLTextureBuffer(bool defaultValue=false) const
qint32 checkSize(bool defaultValue=false) const
bool scrollCheckers(bool defaultValue=false) const
QColor getCursorMainColor(bool defaultValue=false) const
bool separateEraserCursor(bool defaultValue=false) const
QColor getEraserCursorMainColor(bool defaultValue=false) const
QSize viewportDevicePixelSize() const
_Private::Traits< T >::Result widgetToDocument(const T &obj) const
_Private::Traits< T >::Result widgetToViewport(const T &obj) const
void imagePhysicalScale(qreal *scaleX, qreal *scaleY) const
QTransform imageToWidgetTransform() const
void getOpenGLCheckersInfo(const QRectF &viewportRect, QTransform *textureTransform, QTransform *modelTransform, QRectF *textureRect, QRectF *modelRect, const bool scrollCheckers) const
QSizeF getCanvasWidgetSize() const
_Private::Traits< T >::Result widgetToImage(const T &obj) const
_Private::Traits< T >::Result documentToImage(const T &obj) const
QRectF imageRectInViewportPixels() const
_Private::Traits< T >::Result imageToViewport(const T &obj) const
KisDisplayConfig This class keeps track of the color management configuration for image to display....
KoColorConversionTransformation::ConversionFlags conversionFlags
const KoColorProfile * profile
KoColorConversionTransformation::Intent intent
virtual KisCanvas2 * canvas() const =0
virtual QColor borderColor() const =0
virtual KisCoordinatesConverter * coordinatesConverter() const =0
virtual QOpenGLContext * openglContext() const =0
virtual GLenum internalTextureFormat() const =0
virtual qreal devicePixelRatioF() const =0
QRect updateCanvasProjection(KisUpdateInfoSP info)
void initializeDisplayShader()
void setDisplayFilter(QSharedPointer< KisDisplayFilter > displayFilter)
void drawImage(const QRect &updateRect)
void drawBackground(const QRect &updateRect)
QRectF surfaceToWidget(const QRectF &rc)
void resizeGL(int width, int height)
KisOpenGLImageTexturesSP openGLImageTextures() const
void channelSelectionChanged(const QBitArray &channelFlags)
QOpenGLContext * context() const
KisUpdateInfoSP startUpdateCanvasProjection(const QRect &rc)
void paintCanvasOnly(const QRect &canvasImageDirtyRect, const QRect &viewportUpdateRect=QRect())
void setDisplayFilterImpl(QSharedPointer< KisDisplayFilter > displayFilter, bool initializing)
QRectF widgetToSurface(const QRectF &rc)
void drawImageTiles(int firstCol, int lastCol, int firstRow, int lastRow, qreal scaleX, qreal scaleY, const QPoint &wrapAroundOffset)
void drawGrid(const QRect &updateRect)
void reportFailedShaderCompilation(const QString &context)
void setLodResetInProgress(bool value)
void setDisplayConfig(const KisDisplayConfig &config)
WrapAroundAxis wrapAroundViewingModeAxis() const
KisOpenGLCanvasRenderer(CanvasBridge *canvasBridge, KisImageWSP image, const KisDisplayConfig &displayConfig, QSharedPointer< KisDisplayFilter > displayFilter)
void setWrapAroundViewingModeAxis(WrapAroundAxis value)
void drawCheckers(const QRect &updateRect)
bool wrapAroundViewingMode() const
void updatePixelGridMode()
void finishResizingImage(qint32 w, qint32 h)
void paintToolOutline(const KisOptimizedBrushOutline &path, const QRect &viewportUpdateRect, const int thickness=1)
qreal devicePixelRatioF() const
void renderCanvasGL(const QRect &updateRect)
void setWrapAroundViewingMode(bool value)
KisCanvas2 * canvas() const
QColor borderColor() const
KisCoordinatesConverter * coordinatesConverter() const
~KisOpenGLCanvasRenderer()
void notifyImageColorSpaceChanged(const KoColorSpace *cs)
static const int BACKGROUND_TEXTURE_SIZE
int getTextureBufferIndexCR(int col, int row)
KisOpenGLUpdateInfoSP updateCache(const QRect &rect, KisImageSP srcImage)
void generateCheckerTexture(const QImage &checkImage)
QRect storedImageBounds()
const KoColorProfile * monitorProfile()
void initGL(QOpenGLFunctions *f)
void slotImageSizeChanged(qint32 w, qint32 h)
static const int BACKGROUND_TEXTURE_CHECK_SIZE
QOpenGLBuffer * tileVertexBuffer()
void updateConfig(bool useBuffer, int NumMipmapLevels)
void recalculateCache(KisUpdateInfoSP info, bool blockMipmapRegeneration)
QOpenGLBuffer * tileTexCoordBuffer()
bool setImageColorSpace(const KoColorSpace *cs)
static KisOpenGLImageTexturesSP createImageTextures(KisImageWSP image, const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
void setChannelFlags(const QBitArray &channelFlags)
KisTextureTile * getTextureTileCR(int col, int row)
KisOpenGLUpdateInfoBuilder & updateInfoBuilder()
bool setInternalColorManagementActive(bool value, bool iniializing=false)
void setMonitorProfile(const KoColorProfile *monitorProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
void setProofingConfig(KisProofingConfigurationSP)
KisShaderProgram * loadSolidColorShader()
KisShaderProgram * loadCheckerShader()
KisShaderProgram * loadDisplayShader(QSharedPointer< KisDisplayFilter > displayFilter, bool useHiQualityFiltering)
static bool useFBOForToolOutlineRendering()
supportsRenderToFBO
static void initializeContext(QOpenGLContext *ctx)
Initialize shared OpenGL context.
static bool supportsVAO()
int location(Uniform uniform)
QRect tileRectInImagePixels()
QRectF tileRectInTexturePixels()
int bindToActiveTexture(bool blockMipmapRegeneration)
QPointer< KoCanvasResourceProvider > resourceManager
virtual KoID colorDepthId() const =0
virtual void normalisedChannelsValue(const quint8 *pixel, QVector< float > &channels) const =0
void convertTo(const KoColorSpace *cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
const KoColorSpace * colorSpace() const
return the current colorSpace
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
#define SCALE_LESS_THAN(scX, scY, value)
#define SCALE_MORE_OR_EQUAL_TO(scX, scY, value)
T kisGrowRect(const T &rect, U offset)
#define PROGRAM_TEXCOORD_ATTRIBUTE
#define PROGRAM_VERTEX_ATTRIBUTE
auto maxDimension(Size size) -> decltype(size.width())
T wrapValue(T value, T wrapBounds)
Point normalize(const Point &a)
PointTypeTraits< T >::value_type dotProduct(const T &a, const T &b)
void rectToTexCoords(QVector2D *texCoords, const QRectF &rc)
void rectToVertices(QVector3D *vertices, const QRectF &rc)
@ CurrentEffectiveCompositeOp
void allocate(int numBuffers, int bufferSize)
QOpenGLBuffer * getNextBuffer()
KisShaderProgram * checkerShader
KisOpenGLBufferCircularStorage checkersVertexBuffer
QScopedPointer< QOpenGLFramebufferObject > canvasFBO
CanvasBridge * canvasBridge
int yToRowWithWrapCompensation(int y, const QRect &imageRect)
QSizeF pixelAlignedWidgetSize
QOpenGLVertexArrayObject quadVAO
qreal pixelGridDrawingThreshold
WrapAroundAxis wrapAroundModeAxis
QOpenGLVertexArrayObject outlineVAO
KisOpenGL::FilterMode filterMode
KisShaderProgram * displayShader
int xToColWithWrapCompensation(int x, const QRect &imageRect)
KisOpenGLBufferCircularStorage checkersTextureVertexBuffer
KisShaderProgram * solidColorShader
bool proofingConfigIsUpdated
QVector< QVector3D > lineVerticesStagingBuffer
bool displayShaderCompiledWithDisplayFilterSupport
KisOpenGLShaderLoader shaderLoader
QSize viewportDevicePixelSize
QOpenGLBuffer lineVertexBuffer
KisOpenGLImageTexturesSP openGLImageTextures
QSharedPointer< KisDisplayFilter > displayFilter
const KoColorSpace * destinationColorSpace() const
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()