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
122 int colsPerImage = lastImageColumn - firstImageColumn + 1;
123 int numWraps = floor(qreal(x) / imageRect.width());
124 int remainder = x - imageRect.width() * numWraps;
133 int rowsPerImage = lastImageRow - firstImageRow + 1;
134 int numWraps = floor(qreal(y) / imageRect.height());
135 int remainder = y - imageRect.height() * numWraps;
199 bool needsInternalColorManagement =
200 !displayFilter || displayFilter->useInternalColorManagement();
206 if (!initializing && needsFullRefresh) {
209 else if (!initializing) {
247 initializeOpenGLFunctions();
331 qDebug() <<
"Shader Compilation Failure: " <<
context;
333 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"),
334 i18n(
"Krita could not initialize the OpenGL canvas:\n\n%1\n\n Krita will disable OpenGL and close now.",
context),
363 QOpenGLFramebufferObjectFormat format;
372 if (!canvasImageDirtyRect.isEmpty()) {
378 if (viewportUpdateRect.isEmpty()) {
382 blitRect = scale.mapRect(QRectF(viewportUpdateRect)).toAlignedRect();
384 QOpenGLFramebufferObject::blitFramebuffer(
nullptr, blitRect,
d->
canvasFBO.data(), blitRect, GL_COLOR_BUFFER_BIT, GL_NEAREST);
385 QOpenGLFramebufferObject::bindDefault();
387 QRect fullUpdateRect = canvasImageDirtyRect | viewportUpdateRect;
388 if (fullUpdateRect.isEmpty()) {
404 QMatrix4x4 projectionMatrix;
405 projectionMatrix.setToIdentity();
408 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
412 modelMatrix.optimize();
413 modelMatrix = projectionMatrix * modelMatrix;
421 glBlendFuncSeparate(GL_ONE, GL_SRC_COLOR, GL_ONE, GL_ONE);
422 glBlendEquationSeparate(GL_FUNC_SUBTRACT, GL_FUNC_ADD);
425 if (!viewportUpdateRect.isEmpty()) {
426 const QRect deviceUpdateRect =
widgetToSurface(viewportUpdateRect).toAlignedRect();
427 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height());
428 glEnable(GL_SCISSOR_TEST);
447 for (
auto it = path.begin(); it != path.end(); ++it) {
448 const QPolygonF& polygon = *it;
454 int triangleCount = 0;
455 verticesBuffer.clear();
456 const bool closed = polygon.isClosed();
458 for(
int i = 1; i < polygon.count(); i++) {
459 bool adjustFirst = closed?
true: i > 1;
460 bool adjustSecond = closed?
true: i + 1 < polygon.count();
462 QPointF
p1 = polygon.at(i - 1);
463 QPointF
p2 = polygon.at(i);
464 QPointF normal =
p2 -
p1;
467 QPointF c1 =
p1 - (normal * halfWidth);
468 QPointF c2 =
p1 + (normal * halfWidth);
469 QPointF c3 =
p2 - (normal * halfWidth);
470 QPointF c4 =
p2 + (normal * halfWidth);
474 QPointF pPrev = i >= 2 ?
475 QPointF(polygon.at(i-2)) :
476 QPointF(polygon.at(qMax(polygon.count() - 2, 0)));
483 QPointF(-pPrev.y(), pPrev.x())));
488 c1 =
p1 + ((miter * -halfWidth) / dot);
489 c2 =
p1 + ((miter * halfWidth) / dot);
494 QPointF pNext = i + 1 < polygon.count()? QPointF(polygon.at(i+1))
495 : QPointF(polygon.at(qMin(polygon.count(), 1)));
503 c3 =
p2 + ((miter * -halfWidth) / dot);
504 c4 =
p2 + (miter * halfWidth) / dot;
508 verticesBuffer.append(QVector3D(c1));
509 verticesBuffer.append(QVector3D(c3));
510 verticesBuffer.append(QVector3D(c2));
511 verticesBuffer.append(QVector3D(c4));
512 verticesBuffer.append(QVector3D(c2));
513 verticesBuffer.append(QVector3D(c3));
519 d->
lineVertexBuffer.allocate(verticesBuffer.constData(), 3 * verticesBuffer.size() *
sizeof(float));
526 glDrawArrays(GL_TRIANGLES, 0, triangleCount * 3);
530 for (
auto it = path.begin(); it != path.end(); ++it) {
531 const QPolygonF& polygon = *it;
537 const int verticesCount = polygon.count();
539 if (verticesBuffer.size() < verticesCount) {
540 verticesBuffer.resize(verticesCount);
543 for (
int vertIndex = 0; vertIndex < verticesCount; vertIndex++) {
544 QPointF point = polygon.at(vertIndex);
545 verticesBuffer[vertIndex].setX(point.x());
546 verticesBuffer[vertIndex].setY(point.y());
550 d->
lineVertexBuffer.allocate(verticesBuffer.constData(), 3 * verticesCount *
sizeof(float));
559 glDrawArrays(GL_LINE_STRIP, 0, verticesCount);
568 if (!viewportUpdateRect.isEmpty()) {
569 glDisable(GL_SCISSOR_TEST);
572 glBlendEquation(GL_FUNC_ADD);
573 glBlendFunc(GL_ONE, GL_ZERO);
586 Q_UNUSED(updateRect);
597 convertedBackgroundColor.
convertTo(finalColorSpace);
604 glClearColor(channels[2], channels[1], channels[0], 1.0);
605 glClear(GL_COLOR_BUFFER_BIT);
610 Q_UNUSED(updateRect);
617 QTransform textureTransform;
618 QTransform modelTransform;
629 viewportRect = converter->
widgetToViewport(QRectF(0, 0, widgetSize.width(), widgetSize.height()));
631 viewportRect.setTop(ir.top());
632 viewportRect.setBottom(ir.bottom());
635 viewportRect.setLeft(ir.left());
636 viewportRect.setRight(ir.right());
641 if (!
canvas()->renderingLimit().isEmpty()) {
643 viewportRect &= vrect;
647 &textureTransform, &modelTransform, &textureRect, &modelRect,
d->
scrollCheckers);
653 qWarning() <<
"Could not bind checker shader";
657 QMatrix4x4 projectionMatrix;
658 projectionMatrix.setToIdentity();
661 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
664 QMatrix4x4 modelMatrix(modelTransform);
665 modelMatrix.optimize();
666 modelMatrix = projectionMatrix * modelMatrix;
669 QMatrix4x4 textureMatrix(textureTransform);
678 vertexBuf->write(0,
d->
vertices, 3 * 6 *
sizeof(
float));
685 vertexTextureBuf->bind();
686 vertexTextureBuf->write(0,
d->
texCoords, 2 * 6 *
sizeof(
float));
700 glActiveTexture(GL_TEXTURE0);
703 glDrawArrays(GL_TRIANGLES, 0, 6);
705 glBindTexture(GL_TEXTURE_2D, 0);
707 glBindBuffer(GL_ARRAY_BUFFER, 0);
718 QMatrix4x4 projectionMatrix;
719 projectionMatrix.setToIdentity();
722 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
726 modelMatrix.optimize();
727 modelMatrix = projectionMatrix * modelMatrix;
731 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
737 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height());
739 QRect wr = widgetRectInImagePixels.toAlignedRect();
745 if (!updateRect.isEmpty()) {
747 wr &= updateRectInImagePixels;
750 QPoint topLeftCorner = wr.topLeft();
751 QPoint bottomRightCorner = wr.bottomRight() + QPoint(1, 1);
754 for (
int i = topLeftCorner.x(); i <= bottomRightCorner.x(); ++i) {
755 grid.append(QVector3D(i, topLeftCorner.y(), 0));
756 grid.append(QVector3D(i, bottomRightCorner.y(), 0));
758 for (
int i = topLeftCorner.y(); i <= bottomRightCorner.y(); ++i) {
759 grid.append(QVector3D(topLeftCorner.x(), i, 0));
760 grid.append(QVector3D(bottomRightCorner.x(), i, 0));
773 glDrawArrays(GL_LINES, 0, grid.size());
791 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
799 QMatrix4x4 textureMatrix;
800 textureMatrix.setToIdentity();
803 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height());
805 if (!updateRect.isEmpty()) {
806 widgetRect &= updateRect;
813 if (!renderingLimit.isEmpty()) {
814 widgetRectInImagePixels &= renderingLimit;
817 qreal scaleX, scaleY;
824 QRect wr = widgetRectInImagePixels.toAlignedRect();
833 wr.setBottom(ir.bottom());
836 wr.setLeft(ir.left());
837 wr.setRight(ir.right());
850 const int imageColumns = maxColumn - minColumn + 1;
851 const int imageRows = maxRow - minRow + 1;
857 const int firstCloneX = qFloor(qreal(firstColumn) / imageColumns);
858 const int lastCloneX = qFloor(qreal(lastColumn) / imageColumns);
859 const int firstCloneY = qFloor(qreal(firstRow) / imageRows);
860 const int lastCloneY = qFloor(qreal(lastRow) / imageRows);
862 for (
int cloneY = firstCloneY; cloneY <= lastCloneY; cloneY++) {
863 for (
int cloneX = firstCloneX; cloneX <= lastCloneX; cloneX++) {
866 const int localLastCol = cloneX == lastCloneX ?
KisAlgebra2D::wrapValue(lastColumn, imageColumns) : imageColumns - 1;
872 localFirstRow, localLastRow,
873 scaleX, scaleY, QPoint(cloneX, cloneY));
887 QMatrix4x4 projectionMatrix;
888 projectionMatrix.setToIdentity();
891 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
895 if (!wrapAroundOffset.isNull()) {
898 const QTransform wrapAroundTranslate = QTransform::fromTranslate(ir.width() * wrapAroundOffset.x(),
899 ir.height() * wrapAroundOffset.y());
900 modelTransform = wrapAroundTranslate * modelTransform;
904 QMatrix4x4 modelMatrix(modelTransform);
905 modelMatrix.optimize();
906 modelMatrix = projectionMatrix * modelMatrix;
909 int lastTileLodPlane = -1;
911 for (
int col = firstCol; col <= lastCol; col++) {
912 for (
int row = firstRow; row <= lastRow; row++) {
918 warnUI <<
"OpenGL: Trying to paint texture tile but it has not been created yet.";
926 const int vertexRectSize = 6 * 3 *
sizeof(float);
928 glVertexAttribPointer(
PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0,
reinterpret_cast<void*
>(tileIndex * vertexRectSize));
930 const int textureRectSize = 6 * 2 *
sizeof(float);
932 glVertexAttribPointer(
PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0,
reinterpret_cast<void*
>(tileIndex * textureRectSize));
948 glActiveTexture(GL_TEXTURE0);
954 (lastTileLodPlane < 0 || lastTileLodPlane != currentLodPlane)) {
957 (GLfloat) currentLodPlane);
958 lastTileLodPlane = currentLodPlane;
961 if (currentLodPlane > 0) {
962 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
964 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
965 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
967 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
971 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
972 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
975 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
978 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
982 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
984 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
990 glDrawArrays(GL_TRIANGLES, 0, 6);
994 glBindTexture(GL_TEXTURE_2D, 0);
995 glBindBuffer(GL_ARRAY_BUFFER, 0);
1033 return QRectF(rc.x() * ratio,
1036 rc.height() * ratio);
1043 return QRectF(rc.x() / ratio,
1046 rc.height() / ratio);
1066 QRect alignedUpdateRect = updateRect;
1068 if (!updateRect.isEmpty()) {
1069 const QRect deviceUpdateRect =
widgetToSurface(updateRect).toAlignedRect();
1070 alignedUpdateRect =
surfaceToWidget(deviceUpdateRect).toAlignedRect();
1072 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height());
1073 glEnable(GL_SCISSOR_TEST);
1084 if (!updateRect.isEmpty()) {
1085 glDisable(GL_SCISSOR_TEST);
1115 if (
canvas()->proofingConfigUpdated()) {
1127 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)
bool setInternalColorManagementActive(bool value)
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()
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
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()