9#define GL_GLEXT_PROTOTYPES
25#include <QPainterPath>
26#include <QOpenGLPaintDevice>
31#include <QOpenGLShaderProgram>
32#include <QOpenGLVertexArrayObject>
33#include <QOpenGLBuffer>
34#include <QOpenGLFramebufferObject>
35#include <QOpenGLFramebufferObjectFormat>
45#include <config-ocio.h>
47#define NEAR_VAL -1000.0
50#ifndef GL_CLAMP_TO_EDGE
51#define GL_CLAMP_TO_EDGE 0x812F
54#define PROGRAM_VERTEX_ATTRIBUTE 0
55#define PROGRAM_TEXCOORD_ATTRIBUTE 1
124 int colsPerImage = lastImageColumn - firstImageColumn + 1;
125 int numWraps = floor(qreal(x) / imageRect.width());
126 int remainder = x - imageRect.width() * numWraps;
135 int rowsPerImage = lastImageRow - firstImageRow + 1;
136 int numWraps = floor(qreal(y) / imageRect.height());
137 int remainder = y - imageRect.height() * numWraps;
201 bool needsInternalColorManagement =
202 !displayFilter || displayFilter->useInternalColorManagement();
208 if (!initializing && needsFullRefresh) {
211 else if (!initializing) {
249 initializeOpenGLFunctions();
333 qDebug() <<
"Shader Compilation Failure: " <<
context;
335 QMessageBox::critical(qApp->activeWindow(), i18nc(
"@title:window",
"Krita"),
336 i18n(
"Krita could not initialize the OpenGL canvas:\n\n%1\n\n Krita will disable OpenGL and close now.",
context),
365 QOpenGLFramebufferObjectFormat format;
374 if (!canvasImageDirtyRect.isEmpty()) {
380 if (viewportUpdateRect.isEmpty()) {
384 blitRect = scale.mapRect(QRectF(viewportUpdateRect)).toAlignedRect();
386 QOpenGLFramebufferObject::blitFramebuffer(
nullptr, blitRect,
d->
canvasFBO.data(), blitRect, GL_COLOR_BUFFER_BIT, GL_NEAREST);
387 QOpenGLFramebufferObject::bindDefault();
389 QRect fullUpdateRect = canvasImageDirtyRect | viewportUpdateRect;
390 if (fullUpdateRect.isEmpty()) {
406 QMatrix4x4 projectionMatrix;
407 projectionMatrix.setToIdentity();
410 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
414 modelMatrix.optimize();
415 modelMatrix = projectionMatrix * modelMatrix;
423 glBlendFuncSeparate(GL_ONE, GL_SRC_COLOR, GL_ONE, GL_ONE);
424 glBlendEquationSeparate(GL_FUNC_SUBTRACT, GL_FUNC_ADD);
427 if (!viewportUpdateRect.isEmpty()) {
428 const QRect deviceUpdateRect =
widgetToSurface(viewportUpdateRect).toAlignedRect();
429 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height());
430 glEnable(GL_SCISSOR_TEST);
448 for (
auto it = path.begin(); it != path.end(); ++it) {
449 const QPolygonF& polygon = *it;
455 int triangleCount = 0;
458 const bool closed = polygon.isClosed();
460 for(
int i = 1; i < polygon.count(); i++) {
461 bool adjustFirst = closed?
true: i > 1;
462 bool adjustSecond = closed?
true: i + 1 < polygon.count();
464 QPointF
p1 = polygon.at(i - 1);
465 QPointF
p2 = polygon.at(i);
466 QPointF normal =
p2 -
p1;
469 QPointF c1 =
p1 - (normal * halfWidth);
470 QPointF c2 =
p1 + (normal * halfWidth);
471 QPointF c3 =
p2 - (normal * halfWidth);
472 QPointF c4 =
p2 + (normal * halfWidth);
476 QPointF pPrev = i >= 2 ?
477 QPointF(polygon.at(i-2)) :
478 QPointF(polygon.at(qMax(polygon.count() - 2, 0)));
485 QPointF(-pPrev.y(), pPrev.x())));
489 const qreal dotMulti = dot != 0? 1.0/dot: 0.0;
492 c1 =
p1 + ((miter * -halfWidth) * dotMulti);
493 c2 =
p1 + ((miter * halfWidth) * dotMulti);
498 QPointF pNext = i + 1 < polygon.count()? QPointF(polygon.at(i+1))
499 : QPointF(polygon.at(qMin(polygon.count(), 1)));
505 const qreal dotMulti = dot != 0? 1.0/dot: 0.0;
508 c3 =
p2 + ((miter * -halfWidth) * dotMulti);
509 c4 =
p2 + (miter * halfWidth) * dotMulti;
531 glDrawArrays(GL_TRIANGLES, 0, triangleCount * 3);
535 for (
auto it = path.begin(); it != path.end(); ++it) {
536 const QPolygonF& polygon = *it;
542 const int verticesCount = polygon.count();
548 for (
int vertIndex = 0; vertIndex < verticesCount; vertIndex++) {
549 QPointF point = polygon.at(vertIndex);
564 glDrawArrays(GL_LINE_STRIP, 0, verticesCount);
573 if (!viewportUpdateRect.isEmpty()) {
574 glDisable(GL_SCISSOR_TEST);
577 glBlendEquation(GL_FUNC_ADD);
578 glBlendFunc(GL_ONE, GL_ZERO);
591 Q_UNUSED(updateRect);
596 glClearColor(bgColor.redF(), bgColor.greenF(), bgColor.blueF(), 1.0);
597 glClear(GL_COLOR_BUFFER_BIT);
602 Q_UNUSED(updateRect);
609 QTransform textureTransform;
610 QTransform modelTransform;
621 viewportRect = converter->
widgetToViewport(QRectF(0, 0, widgetSize.width(), widgetSize.height()));
623 viewportRect.setTop(ir.top());
624 viewportRect.setBottom(ir.bottom());
627 viewportRect.setLeft(ir.left());
628 viewportRect.setRight(ir.right());
633 if (!
canvas()->renderingLimit().isEmpty()) {
635 viewportRect &= vrect;
639 &textureTransform, &modelTransform, &textureRect, &modelRect,
d->
scrollCheckers);
645 qWarning() <<
"Could not bind checker shader";
649 QMatrix4x4 projectionMatrix;
650 projectionMatrix.setToIdentity();
653 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
656 QMatrix4x4 modelMatrix(modelTransform);
657 modelMatrix.optimize();
658 modelMatrix = projectionMatrix * modelMatrix;
661 QMatrix4x4 textureMatrix(textureTransform);
670 vertexBuf->write(0,
d->
vertices, 3 * 6 *
sizeof(
float));
677 vertexTextureBuf->bind();
678 vertexTextureBuf->write(0,
d->
texCoords, 2 * 6 *
sizeof(
float));
692 glActiveTexture(GL_TEXTURE0);
695 glDrawArrays(GL_TRIANGLES, 0, 6);
697 glBindTexture(GL_TEXTURE_2D, 0);
699 glBindBuffer(GL_ARRAY_BUFFER, 0);
710 QMatrix4x4 projectionMatrix;
711 projectionMatrix.setToIdentity();
714 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
718 modelMatrix.optimize();
719 modelMatrix = projectionMatrix * modelMatrix;
723 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
729 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height());
731 QRect wr = widgetRectInImagePixels.toAlignedRect();
737 if (!updateRect.isEmpty()) {
739 wr &= updateRectInImagePixels;
742 QPoint topLeftCorner = wr.topLeft();
743 QPoint bottomRightCorner = wr.bottomRight() + QPoint(1, 1);
746 for (
int i = topLeftCorner.x(); i <= bottomRightCorner.x(); ++i) {
747 grid.append(QVector3D(i, topLeftCorner.y(), 0));
748 grid.append(QVector3D(i, bottomRightCorner.y(), 0));
750 for (
int i = topLeftCorner.y(); i <= bottomRightCorner.y(); ++i) {
751 grid.append(QVector3D(topLeftCorner.x(), i, 0));
752 grid.append(QVector3D(bottomRightCorner.x(), i, 0));
765 glDrawArrays(GL_LINES, 0, grid.size());
783 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
791 QMatrix4x4 textureMatrix;
792 textureMatrix.setToIdentity();
795 QRectF widgetRect(0,0, widgetSize.width(), widgetSize.height());
797 if (!updateRect.isEmpty()) {
798 widgetRect &= updateRect;
805 if (!renderingLimit.isEmpty()) {
806 widgetRectInImagePixels &= renderingLimit;
809 qreal scaleX, scaleY;
816 QRect wr = widgetRectInImagePixels.toAlignedRect();
825 wr.setBottom(ir.bottom());
828 wr.setLeft(ir.left());
829 wr.setRight(ir.right());
842 const int imageColumns = maxColumn - minColumn + 1;
843 const int imageRows = maxRow - minRow + 1;
849 const int firstCloneX = qFloor(qreal(firstColumn) / imageColumns);
850 const int lastCloneX = qFloor(qreal(lastColumn) / imageColumns);
851 const int firstCloneY = qFloor(qreal(firstRow) / imageRows);
852 const int lastCloneY = qFloor(qreal(lastRow) / imageRows);
854 for (
int cloneY = firstCloneY; cloneY <= lastCloneY; cloneY++) {
855 for (
int cloneX = firstCloneX; cloneX <= lastCloneX; cloneX++) {
858 const int localLastCol = cloneX == lastCloneX ?
KisAlgebra2D::wrapValue(lastColumn, imageColumns) : imageColumns - 1;
864 localFirstRow, localLastRow,
865 scaleX, scaleY, QPoint(cloneX, cloneY));
879 QMatrix4x4 projectionMatrix;
880 projectionMatrix.setToIdentity();
883 projectionMatrix.ortho(0, widgetSize.width(), widgetSize.height(), 0,
NEAR_VAL,
FAR_VAL);
887 if (!wrapAroundOffset.isNull()) {
890 const QTransform wrapAroundTranslate = QTransform::fromTranslate(ir.width() * wrapAroundOffset.x(),
891 ir.height() * wrapAroundOffset.y());
892 modelTransform = wrapAroundTranslate * modelTransform;
896 QMatrix4x4 modelMatrix(modelTransform);
897 modelMatrix.optimize();
898 modelMatrix = projectionMatrix * modelMatrix;
901 int lastTileLodPlane = -1;
903 for (
int col = firstCol; col <= lastCol; col++) {
904 for (
int row = firstRow; row <= lastRow; row++) {
910 warnUI <<
"OpenGL: Trying to paint texture tile but it has not been created yet.";
918 const int vertexRectSize = 6 * 3 *
sizeof(float);
920 glVertexAttribPointer(
PROGRAM_VERTEX_ATTRIBUTE, 3, GL_FLOAT, GL_FALSE, 0,
reinterpret_cast<void*
>(tileIndex * vertexRectSize));
922 const int textureRectSize = 6 * 2 *
sizeof(float);
924 glVertexAttribPointer(
PROGRAM_TEXCOORD_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0,
reinterpret_cast<void*
>(tileIndex * textureRectSize));
940 glActiveTexture(GL_TEXTURE0);
946 (lastTileLodPlane < 0 || lastTileLodPlane != currentLodPlane)) {
949 (GLfloat) currentLodPlane);
950 lastTileLodPlane = currentLodPlane;
953 if (currentLodPlane > 0) {
954 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
956 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
957 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
959 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
963 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
964 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
967 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
970 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
974 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
976 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
982 glDrawArrays(GL_TRIANGLES, 0, 6);
986 glBindTexture(GL_TEXTURE_2D, 0);
987 glBindBuffer(GL_ARRAY_BUFFER, 0);
1026 return QRectF(rc.x() * ratio,
1029 rc.height() * ratio);
1036 return QRectF(rc.x() / ratio,
1039 rc.height() / ratio);
1059 QRect alignedUpdateRect = updateRect;
1061 if (!updateRect.isEmpty()) {
1062 const QRect deviceUpdateRect =
widgetToSurface(updateRect).toAlignedRect();
1063 alignedUpdateRect =
surfaceToWidget(deviceUpdateRect).toAlignedRect();
1065 glScissor(deviceUpdateRect.x(), deviceUpdateRect.y(), deviceUpdateRect.width(), deviceUpdateRect.height());
1066 glEnable(GL_SCISSOR_TEST);
1077 if (!updateRect.isEmpty()) {
1078 glDisable(GL_SCISSOR_TEST);
1115 if (
canvas()->proofingConfigUpdated()) {
1127 if (isOpenGLUpdateInfo) {
float value(const T *src, size_t ch)
static constexpr int NumberOfBuffers
const QString COMPOSITE_ERASE
void startUpdateInPatches(const QRect &imageRect)
KoColorDisplayRendererInterface * displayRendererInterface() const override
displayRendererInterface The display renderer interface has a number of color conversion functions wh...
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
QColor colorToDisplaySpace(const QColor &c)
~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()
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 QColor convertColorToDisplayColorSpace(const KoColor color) const =0
convertColorToDisplayColorSpace
void toQColor(QColor *c) const
a convenience method for the above.
#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
static KoColorSpaceRegistry * instance()