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),
362 QOpenGLFramebufferObjectFormat format;
371 if (!canvasImageDirtyRect.isEmpty()) {
377 if (viewportUpdateRect.isEmpty()) {
381 blitRect = scale.mapRect(QRectF(viewportUpdateRect)).toAlignedRect();
383 QOpenGLFramebufferObject::blitFramebuffer(
nullptr, blitRect,
d->
canvasFBO.data(), blitRect, GL_COLOR_BUFFER_BIT, GL_NEAREST);
384 QOpenGLFramebufferObject::bindDefault();
386 QRect fullUpdateRect = canvasImageDirtyRect | viewportUpdateRect;
387 if (fullUpdateRect.isEmpty()) {
403 QMatrix4x4 projectionMatrix;
404 projectionMatrix.setToIdentity();
407 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
411 modelMatrix.optimize();
412 modelMatrix = projectionMatrix * modelMatrix;
420 glBlendFuncSeparate(GL_ONE, GL_SRC_COLOR, GL_ONE, GL_ONE);
421 glBlendEquationSeparate(GL_FUNC_SUBTRACT, GL_FUNC_ADD);
424 if (!viewportUpdateRect.isEmpty()) {
425 const QRect deviceUpdateRect =
widgetToSurface(viewportUpdateRect).toAlignedRect();
426 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height());
427 glEnable(GL_SCISSOR_TEST);
446 for (
auto it = path.begin(); it != path.end(); ++it) {
447 const QPolygonF& polygon = *it;
453 int triangleCount = 0;
454 verticesBuffer.clear();
455 const bool closed = polygon.isClosed();
457 for(
int i = 1; i < polygon.count(); i++) {
458 bool adjustFirst = closed?
true: i > 1;
459 bool adjustSecond = closed?
true: i + 1 < polygon.count();
461 QPointF
p1 = polygon.at(i - 1);
462 QPointF
p2 = polygon.at(i);
463 QPointF normal =
p2 -
p1;
466 QPointF c1 =
p1 - (normal * halfWidth);
467 QPointF c2 =
p1 + (normal * halfWidth);
468 QPointF c3 =
p2 - (normal * halfWidth);
469 QPointF c4 =
p2 + (normal * halfWidth);
473 QPointF pPrev = i >= 2 ?
474 QPointF(polygon.at(i-2)) :
475 QPointF(polygon.at(qMax(polygon.count() - 2, 0)));
482 QPointF(-pPrev.y(), pPrev.x())));
487 c1 =
p1 + ((miter * -halfWidth) / dot);
488 c2 =
p1 + ((miter * halfWidth) / dot);
493 QPointF pNext = i + 1 < polygon.count()? QPointF(polygon.at(i+1))
494 : QPointF(polygon.at(qMin(polygon.count(), 1)));
502 c3 =
p2 + ((miter * -halfWidth) / dot);
503 c4 =
p2 + (miter * halfWidth) / dot;
507 verticesBuffer.append(QVector3D(c1));
508 verticesBuffer.append(QVector3D(c3));
509 verticesBuffer.append(QVector3D(c2));
510 verticesBuffer.append(QVector3D(c4));
511 verticesBuffer.append(QVector3D(c2));
512 verticesBuffer.append(QVector3D(c3));
518 d->
lineVertexBuffer.allocate(verticesBuffer.constData(), 3 * verticesBuffer.size() *
sizeof(float));
525 glDrawArrays(GL_TRIANGLES, 0, triangleCount * 3);
529 for (
auto it = path.begin(); it != path.end(); ++it) {
530 const QPolygonF& polygon = *it;
536 const int verticesCount = polygon.count();
538 if (verticesBuffer.size() < verticesCount) {
539 verticesBuffer.resize(verticesCount);
542 for (
int vertIndex = 0; vertIndex < verticesCount; vertIndex++) {
543 QPointF point = polygon.at(vertIndex);
544 verticesBuffer[vertIndex].setX(point.x());
545 verticesBuffer[vertIndex].setY(point.y());
549 d->
lineVertexBuffer.allocate(verticesBuffer.constData(), 3 * verticesCount *
sizeof(float));
558 glDrawArrays(GL_LINE_STRIP, 0, verticesCount);
567 if (!viewportUpdateRect.isEmpty()) {
568 glDisable(GL_SCISSOR_TEST);
571 glBlendEquation(GL_FUNC_ADD);
572 glBlendFunc(GL_ONE, GL_ZERO);
585 Q_UNUSED(updateRect);
596 convertedBackgroundColor.
convertTo(finalColorSpace);
603 glClearColor(channels[2], channels[1], channels[0], 1.0);
604 glClear(GL_COLOR_BUFFER_BIT);
609 Q_UNUSED(updateRect);
616 QTransform textureTransform;
617 QTransform modelTransform;
628 viewportRect = converter->
widgetToViewport(QRectF(0, 0, widgetSize.width(), widgetSize.height()));
630 viewportRect.setTop(ir.top());
631 viewportRect.setBottom(ir.bottom());
634 viewportRect.setLeft(ir.left());
635 viewportRect.setRight(ir.right());
640 if (!
canvas()->renderingLimit().isEmpty()) {
642 viewportRect &= vrect;
646 &textureTransform, &modelTransform, &textureRect, &modelRect,
d->
scrollCheckers);
652 qWarning() <<
"Could not bind checker shader";
656 QMatrix4x4 projectionMatrix;
657 projectionMatrix.setToIdentity();
660 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
663 QMatrix4x4 modelMatrix(modelTransform);
664 modelMatrix.optimize();
665 modelMatrix = projectionMatrix * modelMatrix;
668 QMatrix4x4 textureMatrix(textureTransform);
677 vertexBuf->write(0,
d->
vertices, 3 * 6 *
sizeof(
float));
684 vertexTextureBuf->bind();
685 vertexTextureBuf->write(0,
d->
texCoords, 2 * 6 *
sizeof(
float));
699 glActiveTexture(GL_TEXTURE0);
702 glDrawArrays(GL_TRIANGLES, 0, 6);
704 glBindTexture(GL_TEXTURE_2D, 0);
706 glBindBuffer(GL_ARRAY_BUFFER, 0);
717 QMatrix4x4 projectionMatrix;
718 projectionMatrix.setToIdentity();
721 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
725 modelMatrix.optimize();
726 modelMatrix = projectionMatrix * modelMatrix;
730 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
736 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height());
738 QRect wr = widgetRectInImagePixels.toAlignedRect();
744 if (!updateRect.isEmpty()) {
746 wr &= updateRectInImagePixels;
749 QPoint topLeftCorner = wr.topLeft();
750 QPoint bottomRightCorner = wr.bottomRight() + QPoint(1, 1);
753 for (
int i = topLeftCorner.x(); i <= bottomRightCorner.x(); ++i) {
754 grid.append(QVector3D(i, topLeftCorner.y(), 0));
755 grid.append(QVector3D(i, bottomRightCorner.y(), 0));
757 for (
int i = topLeftCorner.y(); i <= bottomRightCorner.y(); ++i) {
758 grid.append(QVector3D(topLeftCorner.x(), i, 0));
759 grid.append(QVector3D(bottomRightCorner.x(), i, 0));
772 glDrawArrays(GL_LINES, 0, grid.size());
790 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
798 QMatrix4x4 textureMatrix;
799 textureMatrix.setToIdentity();
802 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height());
804 if (!updateRect.isEmpty()) {
805 widgetRect &= updateRect;
812 if (!renderingLimit.isEmpty()) {
813 widgetRectInImagePixels &= renderingLimit;
816 qreal scaleX, scaleY;
823 QRect wr = widgetRectInImagePixels.toAlignedRect();
832 wr.setBottom(ir.bottom());
835 wr.setLeft(ir.left());
836 wr.setRight(ir.right());
849 const int imageColumns = maxColumn - minColumn + 1;
850 const int imageRows = maxRow - minRow + 1;
856 const int firstCloneX = qFloor(qreal(firstColumn) / imageColumns);
857 const int lastCloneX = qFloor(qreal(lastColumn) / imageColumns);
858 const int firstCloneY = qFloor(qreal(firstRow) / imageRows);
859 const int lastCloneY = qFloor(qreal(lastRow) / imageRows);
861 for (
int cloneY = firstCloneY; cloneY <= lastCloneY; cloneY++) {
862 for (
int cloneX = firstCloneX; cloneX <= lastCloneX; cloneX++) {
865 const int localLastCol = cloneX == lastCloneX ?
KisAlgebra2D::wrapValue(lastColumn, imageColumns) : imageColumns - 1;
871 localFirstRow, localLastRow,
872 scaleX, scaleY, QPoint(cloneX, cloneY));
886 QMatrix4x4 projectionMatrix;
887 projectionMatrix.setToIdentity();
890 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
894 if (!wrapAroundOffset.isNull()) {
897 const QTransform wrapAroundTranslate = QTransform::fromTranslate(ir.width() * wrapAroundOffset.x(),
898 ir.height() * wrapAroundOffset.y());
899 modelTransform = wrapAroundTranslate * modelTransform;
903 QMatrix4x4 modelMatrix(modelTransform);
904 modelMatrix.optimize();
905 modelMatrix = projectionMatrix * modelMatrix;
908 int lastTileLodPlane = -1;
910 for (
int col = firstCol; col <= lastCol; col++) {
911 for (
int row = firstRow; row <= lastRow; row++) {
917 warnUI <<
"OpenGL: Trying to paint texture tile but it has not been created yet.";
925 const int vertexRectSize = 6 * 3 *
sizeof(float);
927 glVertexAttribPointer(
PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0,
reinterpret_cast<void*
>(tileIndex * vertexRectSize));
929 const int textureRectSize = 6 * 2 *
sizeof(float);
931 glVertexAttribPointer(
PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0,
reinterpret_cast<void*
>(tileIndex * textureRectSize));
947 glActiveTexture(GL_TEXTURE0);
953 (lastTileLodPlane < 0 || lastTileLodPlane != currentLodPlane)) {
956 (GLfloat) currentLodPlane);
957 lastTileLodPlane = currentLodPlane;
960 if (currentLodPlane > 0) {
961 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
963 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
964 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
966 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
970 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
971 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
974 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
977 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
981 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
983 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
989 glDrawArrays(GL_TRIANGLES, 0, 6);
993 glBindTexture(GL_TEXTURE_2D, 0);
994 glBindBuffer(GL_ARRAY_BUFFER, 0);
1032 return QRectF(rc.x() * ratio,
1035 rc.height() * ratio);
1042 return QRectF(rc.x() / ratio,
1045 rc.height() / ratio);
1065 QRect alignedUpdateRect = updateRect;
1067 if (!updateRect.isEmpty()) {
1068 const QRect deviceUpdateRect =
widgetToSurface(updateRect).toAlignedRect();
1069 alignedUpdateRect =
surfaceToWidget(deviceUpdateRect).toAlignedRect();
1071 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height());
1072 glEnable(GL_SCISSOR_TEST);
1083 if (!updateRect.isEmpty()) {
1084 glDisable(GL_SCISSOR_TEST);
1114 if (
canvas()->proofingConfigUpdated()) {
1126 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()