summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/gui/painting/painting.pri2
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp191
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadersource_p.h66
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp58
-rw-r--r--src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h2
-rw-r--r--src/opengl/qglshaderprogram.cpp67
-rw-r--r--src/opengl/qglshaderprogram.h4
-rw-r--r--src/plugins/imageformats/jpeg/qjpeghandler.cpp285
-rw-r--r--src/plugins/imageformats/jpeg/qjpeghandler.h3
-rw-r--r--tests/auto/qimagereader/tst_qimagereader.cpp10
10 files changed, 451 insertions, 237 deletions
diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
index 0b1e79a..a6cc9c7 100644
--- a/src/gui/painting/painting.pri
+++ b/src/gui/painting/painting.pri
@@ -383,7 +383,7 @@ neon {
DEFINES += QT_HAVE_NEON
HEADERS += painting/qdrawhelper_neon_p.h
SOURCES += painting/qdrawhelper_neon.cpp
- QMAKE.CXXFLAGS *= -mfpu=neon
+ QMAKE_CXXFLAGS *= -mfpu=neon
}
contains(QT_CONFIG, zlib) {
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index 1187c2d..9d545b9 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -170,13 +170,15 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
source.append(qShaderSnippets[MainVertexShader]);
source.append(qShaderSnippets[PositionOnlyVertexShader]);
vertexShader = new QGLShader(QGLShader::Vertex, context, this);
- vertexShader->compileSourceCode(source);
+ if (!vertexShader->compileSourceCode(source))
+ qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile");
source.clear();
source.append(qShaderSnippets[MainFragmentShader]);
source.append(qShaderSnippets[ShockingPinkSrcFragmentShader]);
fragShader = new QGLShader(QGLShader::Fragment, context, this);
- fragShader->compileSourceCode(source);
+ if (!fragShader->compileSourceCode(source))
+ qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile");
simpleShaderProg = new QGLShaderProgram(context, this);
simpleShaderProg->addShader(vertexShader);
@@ -193,13 +195,15 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
source.append(qShaderSnippets[MainWithTexCoordsVertexShader]);
source.append(qShaderSnippets[UntransformedPositionVertexShader]);
vertexShader = new QGLShader(QGLShader::Vertex, context, this);
- vertexShader->compileSourceCode(source);
+ if (!vertexShader->compileSourceCode(source))
+ qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile");
source.clear();
source.append(qShaderSnippets[MainFragmentShader]);
source.append(qShaderSnippets[ImageSrcFragmentShader]);
fragShader = new QGLShader(QGLShader::Fragment, context, this);
- fragShader->compileSourceCode(source);
+ if (!fragShader->compileSourceCode(source))
+ qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile");
blitShaderProg = new QGLShaderProgram(context, this);
blitShaderProg->addShader(vertexShader);
@@ -234,84 +238,95 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
}
}
- QByteArray source;
- source.append(qShaderSnippets[prog.mainFragShader]);
- source.append(qShaderSnippets[prog.srcPixelFragShader]);
- if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
- source.append(prog.customStageSource);
- if (prog.compositionFragShader)
- source.append(qShaderSnippets[prog.compositionFragShader]);
- if (prog.maskFragShader)
- source.append(qShaderSnippets[prog.maskFragShader]);
- QGLShader* fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), this);
- fragShader->compileSourceCode(source);
-
- source.clear();
- source.append(qShaderSnippets[prog.mainVertexShader]);
- source.append(qShaderSnippets[prog.positionVertexShader]);
- QGLShader* vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), this);
- vertexShader->compileSourceCode(source);
+ QGLShader *vertexShader = 0;
+ QGLShader *fragShader = 0;
+ QGLEngineShaderProg *newProg = 0;
+ bool success = false;
+
+ do {
+ QByteArray source;
+ source.append(qShaderSnippets[prog.mainFragShader]);
+ source.append(qShaderSnippets[prog.srcPixelFragShader]);
+ if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
+ source.append(prog.customStageSource);
+ if (prog.compositionFragShader)
+ source.append(qShaderSnippets[prog.compositionFragShader]);
+ if (prog.maskFragShader)
+ source.append(qShaderSnippets[prog.maskFragShader]);
+ fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), this);
+ QByteArray description;
+#if defined(QT_DEBUG)
+ // Name the shader for easier debugging
+ description.append("Fragment shader: main=");
+ description.append(snippetNameStr(prog.mainFragShader));
+ description.append(", srcPixel=");
+ description.append(snippetNameStr(prog.srcPixelFragShader));
+ if (prog.compositionFragShader) {
+ description.append(", composition=");
+ description.append(snippetNameStr(prog.compositionFragShader));
+ }
+ if (prog.maskFragShader) {
+ description.append(", mask=");
+ description.append(snippetNameStr(prog.maskFragShader));
+ }
+ fragShader->setObjectName(QString::fromLatin1(description));
+#endif
+ if (!fragShader->compileSourceCode(source)) {
+ qWarning() << "Warning:" << description << "failed to compile!";
+ break;
+ }
+ source.clear();
+ source.append(qShaderSnippets[prog.mainVertexShader]);
+ source.append(qShaderSnippets[prog.positionVertexShader]);
+ vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), this);
#if defined(QT_DEBUG)
- // Name the shaders for easier debugging
- QByteArray description;
- description.append("Fragment shader: main=");
- description.append(snippetNameStr(prog.mainFragShader));
- description.append(", srcPixel=");
- description.append(snippetNameStr(prog.srcPixelFragShader));
- if (prog.compositionFragShader) {
- description.append(", composition=");
- description.append(snippetNameStr(prog.compositionFragShader));
- }
- if (prog.maskFragShader) {
- description.append(", mask=");
- description.append(snippetNameStr(prog.maskFragShader));
- }
- fragShader->setObjectName(QString::fromLatin1(description));
-
- description.clear();
- description.append("Vertex shader: main=");
- description.append(snippetNameStr(prog.mainVertexShader));
- description.append(", position=");
- description.append(snippetNameStr(prog.positionVertexShader));
- vertexShader->setObjectName(QString::fromLatin1(description));
+ // Name the shader for easier debugging
+ description.clear();
+ description.append("Vertex shader: main=");
+ description.append(snippetNameStr(prog.mainVertexShader));
+ description.append(", position=");
+ description.append(snippetNameStr(prog.positionVertexShader));
+ vertexShader->setObjectName(QString::fromLatin1(description));
#endif
+ if (!vertexShader->compileSourceCode(source)) {
+ qWarning() << "Warning:" << description << "failed to compile!";
+ break;
+ }
- QGLEngineShaderProg* newProg = new QGLEngineShaderProg(prog);
-
- // If the shader program's not found in the cache, create it now.
- newProg->program = new QGLShaderProgram(ctxGuard.context(), this);
- newProg->program->addShader(vertexShader);
- newProg->program->addShader(fragShader);
-
- // We have to bind the vertex attribute names before the program is linked:
- newProg->program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
- if (newProg->useTextureCoords)
- newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
- if (newProg->useOpacityAttribute)
- newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
-
- newProg->program->link();
- if (!newProg->program->isLinked()) {
- QLatin1String none("none");
- QLatin1String br("\n");
- QString error;
- error = QLatin1String("Shader program failed to link,")
+ newProg = new QGLEngineShaderProg(prog);
+
+ // If the shader program's not found in the cache, create it now.
+ newProg->program = new QGLShaderProgram(ctxGuard.context(), this);
+ newProg->program->addShader(vertexShader);
+ newProg->program->addShader(fragShader);
+
+ // We have to bind the vertex attribute names before the program is linked:
+ newProg->program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
+ if (newProg->useTextureCoords)
+ newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
+ if (newProg->useOpacityAttribute)
+ newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
+
+ newProg->program->link();
+ if (!newProg->program->isLinked()) {
+ QLatin1String none("none");
+ QLatin1String br("\n");
+ QString error;
+ error = QLatin1String("Shader program failed to link,")
#if defined(QT_DEBUG)
- + br
- + QLatin1String(" Shaders Used:") + br
- + QLatin1String(" ") + vertexShader->objectName() + QLatin1String(": ") + br
- + QLatin1String(vertexShader->sourceCode()) + br
- + QLatin1String(" ") + fragShader->objectName() + QLatin1String(": ") + br
- + QLatin1String(fragShader->sourceCode()) + br
+ + br
+ + QLatin1String(" Shaders Used:") + br
+ + QLatin1String(" ") + vertexShader->objectName() + QLatin1String(": ") + br
+ + QLatin1String(vertexShader->sourceCode()) + br
+ + QLatin1String(" ") + fragShader->objectName() + QLatin1String(": ") + br
+ + QLatin1String(fragShader->sourceCode()) + br
#endif
- + QLatin1String(" Error Log:\n")
- + QLatin1String(" ") + newProg->program->log();
- qWarning() << error;
- delete newProg; // Deletes the QGLShaderProgram in it's destructor
- newProg = 0;
- }
- else {
+ + QLatin1String(" Error Log:\n")
+ + QLatin1String(" ") + newProg->program->log();
+ qWarning() << error;
+ break;
+ }
if (cachedPrograms.count() > 30) {
// The cache is full, so delete the last 5 programs in the list.
// These programs will be least used, as a program us bumped to
@@ -323,6 +338,22 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
}
cachedPrograms.insert(0, newProg);
+
+ success = true;
+ } while (false);
+
+ // Clean up everything if we weren't successful
+ if (!success) {
+ if (newProg) {
+ delete newProg; // Also deletes the QGLShaderProgram which in turn deletes the QGLShaders
+ newProg = 0;
+ }
+ else {
+ if (vertexShader)
+ delete vertexShader;
+ if (fragShader)
+ delete fragShader;
+ }
}
return newProg;
@@ -364,6 +395,9 @@ QGLEngineShaderManager::~QGLEngineShaderManager()
uint QGLEngineShaderManager::getUniformLocation(Uniform id)
{
+ if (!currentShaderProg)
+ return 0;
+
QVector<uint> &uniformLocations = currentShaderProg->uniformLocations;
if (uniformLocations.isEmpty())
uniformLocations.fill(GLuint(-1), NumUniforms);
@@ -468,7 +502,10 @@ void QGLEngineShaderManager::removeCustomStage()
QGLShaderProgram* QGLEngineShaderManager::currentProgram()
{
- return currentShaderProg->program;
+ if (currentShaderProg)
+ return currentShaderProg->program;
+ else
+ return simpleProgram();
}
QGLShaderProgram* QGLEngineShaderManager::simpleProgram()
diff --git a/src/opengl/gl2paintengineex/qglengineshadersource_p.h b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
index 2407979..46de124 100644
--- a/src/opengl/gl2paintengineex/qglengineshadersource_p.h
+++ b/src/opengl/gl2paintengineex/qglengineshadersource_p.h
@@ -99,12 +99,15 @@ static const char* const qglslMainWithTexCoordsAndOpacityVertexShader = "\
opacity = opacityArray; \
}";
+// NOTE: We let GL do the perspective correction so texture lookups in the fragment
+// shader are also perspective corrected.
static const char* const qglslPositionOnlyVertexShader = "\
- attribute highp vec4 vertexCoordsArray;\
- uniform highp mat4 pmvMatrix;\
+ attribute highp vec2 vertexCoordsArray;\
+ uniform highp mat3 pmvMatrix;\
void setPosition(void)\
{\
- gl_Position = pmvMatrix * vertexCoordsArray;\
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \
+ gl_Position = vec4(transformedPos.xy, 0.0, transformedPos.z); \
}";
static const char* const qglslUntransformedPositionVertexShader = "\
@@ -116,20 +119,19 @@ static const char* const qglslUntransformedPositionVertexShader = "\
// Pattern Brush - This assumes the texture size is 8x8 and thus, the inverted size is 0.125
static const char* const qglslPositionWithPatternBrushVertexShader = "\
- attribute highp vec4 vertexCoordsArray; \
- uniform highp mat4 pmvMatrix; \
+ attribute highp vec2 vertexCoordsArray; \
+ uniform highp mat3 pmvMatrix; \
uniform mediump vec2 halfViewportSize; \
uniform highp vec2 invertedTextureSize; \
uniform highp mat3 brushTransform; \
varying highp vec2 patternTexCoords; \
void setPosition(void) { \
- gl_Position = pmvMatrix * vertexCoordsArray;\
- gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \
mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
- mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
+ mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1.0); \
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
- gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
- gl_Position.w = invertedHTexCoordsZ; \
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \
patternTexCoords.xy = (hTexCoords.xy * 0.125) * invertedHTexCoordsZ; \
}";
@@ -147,20 +149,19 @@ static const char* const qglslPatternBrushSrcFragmentShader = "\
// Linear Gradient Brush
static const char* const qglslPositionWithLinearGradientBrushVertexShader = "\
- attribute highp vec4 vertexCoordsArray; \
- uniform highp mat4 pmvMatrix; \
+ attribute highp vec2 vertexCoordsArray; \
+ uniform highp mat3 pmvMatrix; \
uniform mediump vec2 halfViewportSize; \
uniform highp vec3 linearData; \
uniform highp mat3 brushTransform; \
varying mediump float index; \
void setPosition() { \
- gl_Position = pmvMatrix * vertexCoordsArray;\
- gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \
mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
- gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
- gl_Position.w = invertedHTexCoordsZ; \
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \
index = (dot(linearData.xy, hTexCoords.xy) * linearData.z) * invertedHTexCoordsZ; \
}";
@@ -178,20 +179,19 @@ static const char* const qglslLinearGradientBrushSrcFragmentShader = "\
// Conical Gradient Brush
static const char* const qglslPositionWithConicalGradientBrushVertexShader = "\
- attribute highp vec4 vertexCoordsArray;\
- uniform highp mat4 pmvMatrix;\
+ attribute highp vec2 vertexCoordsArray;\
+ uniform highp mat3 pmvMatrix;\
uniform mediump vec2 halfViewportSize; \
uniform highp mat3 brushTransform; \
varying highp vec2 A; \
void setPosition(void)\
{\
- gl_Position = pmvMatrix * vertexCoordsArray;\
- gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \
mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
- gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
- gl_Position.w = invertedHTexCoordsZ; \
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \
A = hTexCoords.xy * invertedHTexCoordsZ; \
}";
@@ -215,8 +215,8 @@ static const char* const qglslConicalGradientBrushSrcFragmentShader = "\n\
// Radial Gradient Brush
static const char* const qglslPositionWithRadialGradientBrushVertexShader = "\
- attribute highp vec4 vertexCoordsArray;\
- uniform highp mat4 pmvMatrix;\
+ attribute highp vec2 vertexCoordsArray;\
+ uniform highp mat3 pmvMatrix;\
uniform mediump vec2 halfViewportSize; \
uniform highp mat3 brushTransform; \
uniform highp vec2 fmp; \
@@ -224,13 +224,12 @@ static const char* const qglslPositionWithRadialGradientBrushVertexShader = "\
varying highp vec2 A; \
void setPosition(void) \
{\
- gl_Position = pmvMatrix * vertexCoordsArray;\
- gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \
mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
- gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
- gl_Position.w = invertedHTexCoordsZ; \
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \
A = hTexCoords.xy * invertedHTexCoordsZ; \
b = 2.0 * dot(A, fmp); \
}";
@@ -253,20 +252,19 @@ static const char* const qglslRadialGradientBrushSrcFragmentShader = "\
// Texture Brush
static const char* const qglslPositionWithTextureBrushVertexShader = "\
- attribute highp vec4 vertexCoordsArray; \
- uniform highp mat4 pmvMatrix; \
+ attribute highp vec2 vertexCoordsArray; \
+ uniform highp mat3 pmvMatrix; \
uniform mediump vec2 halfViewportSize; \
uniform highp vec2 invertedTextureSize; \
uniform highp mat3 brushTransform; \
varying highp vec2 textureCoords; \
void setPosition(void) { \
- gl_Position = pmvMatrix * vertexCoordsArray;\
- gl_Position.xy = gl_Position.xy / gl_Position.w; \
+ vec3 transformedPos = pmvMatrix * vec3(vertexCoordsArray.xy, 1.0); \
+ gl_Position.xy = transformedPos.xy / transformedPos.z; \
mediump vec2 viewportCoords = (gl_Position.xy + 1.0) * halfViewportSize; \
mediump vec3 hTexCoords = brushTransform * vec3(viewportCoords, 1); \
mediump float invertedHTexCoordsZ = 1.0 / hTexCoords.z; \
- gl_Position.xy = gl_Position.xy * invertedHTexCoordsZ; \
- gl_Position.w = invertedHTexCoordsZ; \
+ gl_Position = vec4(gl_Position.xy * invertedHTexCoordsZ, 0.0, invertedHTexCoordsZ); \
textureCoords.xy = (hTexCoords.xy * invertedTextureSize) * gl_Position.w; \
}";
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
index 8ca2fd4..f52ed92 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2.cpp
@@ -583,37 +583,37 @@ void QGL2PaintEngineExPrivate::updateMatrix()
{
// qDebug("QGL2PaintEngineExPrivate::updateMatrix()");
- // We set up the 4x4 transformation matrix on the vertex shaders to
- // be the equivalent of glOrtho(0, w, h, 0, -1, 1) * transform:
- //
- // | 2/width 0 0 -1 | | m11 m21 0 dx |
- // | 0 -2/height 0 1 | | m12 m22 0 dy |
- // | 0 0 -1 0 | * | 0 0 1 0 |
- // | 0 0 0 1 | | m13 m23 0 m33 |
- //
- // We expand out the multiplication to save the cost of a full 4x4
- // matrix multiplication as most of the components are trivial.
const QTransform& transform = q->state()->matrix;
- qreal wfactor = 2.0 / width;
- qreal hfactor = -2.0 / height;
-
- pmvMatrix[0][0] = wfactor * transform.m11() - transform.m13();
- pmvMatrix[0][1] = hfactor * transform.m12() + transform.m13();
- pmvMatrix[0][2] = 0.0;
- pmvMatrix[0][3] = transform.m13();
- pmvMatrix[1][0] = wfactor * transform.m21() - transform.m23();
- pmvMatrix[1][1] = hfactor * transform.m22() + transform.m23();
- pmvMatrix[1][2] = 0.0;
- pmvMatrix[1][3] = transform.m23();
- pmvMatrix[2][0] = 0.0;
- pmvMatrix[2][1] = 0.0;
- pmvMatrix[2][2] = -1.0;
- pmvMatrix[2][3] = 0.0;
- pmvMatrix[3][0] = wfactor * transform.dx() - transform.m33();
- pmvMatrix[3][1] = hfactor * transform.dy() + transform.m33();
- pmvMatrix[3][2] = 0.0;
- pmvMatrix[3][3] = transform.m33();
+ // The projection matrix converts from Qt's coordinate system to GL's coordinate system
+ // * GL's viewport is 2x2, Qt's is width x height
+ // * GL has +y -> -y going from bottom -> top, Qt is the other way round
+ // * GL has [0,0] in the center, Qt has it in the top-left
+ //
+ // This results in the Projection matrix below, which is multiplied by the painter's
+ // transformation matrix, as shown below:
+ //
+ // Projection Matrix Painter Transform
+ // ------------------------------------------------ ------------------------
+ // | 2.0 / width | 0.0 | -1.0 | | m11 | m21 | dx |
+ // | 0.0 | -2.0 / height | 1.0 | * | m12 | m22 | dy |
+ // | 0.0 | 0.0 | 1.0 | | m13 | m23 | m33 |
+ // ------------------------------------------------ ------------------------
+ //
+ // NOTE: The resultant matrix is also transposed, as GL expects column-major matracies
+
+ const GLfloat wfactor = 2.0f / width;
+ const GLfloat hfactor = -2.0f / height;
+
+ pmvMatrix[0][0] = (wfactor * transform.m11()) - transform.m13();
+ pmvMatrix[1][0] = (wfactor * transform.m21()) - transform.m23();
+ pmvMatrix[2][0] = (wfactor * transform.dx() ) - transform.m33();
+ pmvMatrix[0][1] = (hfactor * transform.m12()) + transform.m13();
+ pmvMatrix[1][1] = (hfactor * transform.m22()) + transform.m23();
+ pmvMatrix[2][1] = (hfactor * transform.dy() ) + transform.m33();
+ pmvMatrix[0][2] = transform.m13();
+ pmvMatrix[1][2] = transform.m23();
+ pmvMatrix[2][2] = transform.m33();
// 1/10000 == 0.0001, so we have good enough res to cover curves
// that span the entire widget...
diff --git a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
index 33ce24d..f1ec6e6 100644
--- a/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
+++ b/src/opengl/gl2paintengineex/qpaintengineex_opengl2_p.h
@@ -254,7 +254,7 @@ public:
GLfloat staticVertexCoordinateArray[8];
GLfloat staticTextureCoordinateArray[8];
- GLfloat pmvMatrix[4][4];
+ GLfloat pmvMatrix[3][3];
QGLEngineShaderManager* shaderManager;
diff --git a/src/opengl/qglshaderprogram.cpp b/src/opengl/qglshaderprogram.cpp
index b4191dc..f9737a56 100644
--- a/src/opengl/qglshaderprogram.cpp
+++ b/src/opengl/qglshaderprogram.cpp
@@ -2275,6 +2275,42 @@ void QGLShaderProgram::setUniformValue(const char *name, const QMatrix4x4& value
\overload
Sets the uniform variable at \a location in the current context
+ to a 2x2 matrix \a value. The matrix elements must be specified
+ in column-major order.
+
+ \sa setAttributeValue()
+ \since 4.6.2
+*/
+void QGLShaderProgram::setUniformValue(int location, const GLfloat value[2][2])
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glUniformMatrix2fv(location, 1, GL_FALSE, value[0]);
+}
+
+/*!
+ \overload
+
+ Sets the uniform variable at \a location in the current context
+ to a 3x3 matrix \a value. The matrix elements must be specified
+ in column-major order.
+
+ \sa setAttributeValue()
+ \since 4.6.2
+*/
+void QGLShaderProgram::setUniformValue(int location, const GLfloat value[3][3])
+{
+ Q_D(QGLShaderProgram);
+ Q_UNUSED(d);
+ if (location != -1)
+ glUniformMatrix3fv(location, 1, GL_FALSE, value[0]);
+}
+
+/*!
+ \overload
+
+ Sets the uniform variable at \a location in the current context
to a 4x4 matrix \a value. The matrix elements must be specified
in column-major order.
@@ -2288,6 +2324,37 @@ void QGLShaderProgram::setUniformValue(int location, const GLfloat value[4][4])
glUniformMatrix4fv(location, 1, GL_FALSE, value[0]);
}
+
+/*!
+ \overload
+
+ Sets the uniform variable called \a name in the current context
+ to a 2x2 matrix \a value. The matrix elements must be specified
+ in column-major order.
+
+ \sa setAttributeValue()
+ \since 4.6.2
+*/
+void QGLShaderProgram::setUniformValue(const char *name, const GLfloat value[2][2])
+{
+ setUniformValue(uniformLocation(name), value);
+}
+
+/*!
+ \overload
+
+ Sets the uniform variable called \a name in the current context
+ to a 3x3 matrix \a value. The matrix elements must be specified
+ in column-major order.
+
+ \sa setAttributeValue()
+ \since 4.6.2
+*/
+void QGLShaderProgram::setUniformValue(const char *name, const GLfloat value[3][3])
+{
+ setUniformValue(uniformLocation(name), value);
+}
+
/*!
\overload
diff --git a/src/opengl/qglshaderprogram.h b/src/opengl/qglshaderprogram.h
index deeaee2..4eb80dd 100644
--- a/src/opengl/qglshaderprogram.h
+++ b/src/opengl/qglshaderprogram.h
@@ -216,6 +216,8 @@ public:
void setUniformValue(int location, const QMatrix4x2& value);
void setUniformValue(int location, const QMatrix4x3& value);
void setUniformValue(int location, const QMatrix4x4& value);
+ void setUniformValue(int location, const GLfloat value[2][2]);
+ void setUniformValue(int location, const GLfloat value[3][3]);
void setUniformValue(int location, const GLfloat value[4][4]);
void setUniformValue(int location, const QTransform& value);
@@ -242,6 +244,8 @@ public:
void setUniformValue(const char *name, const QMatrix4x2& value);
void setUniformValue(const char *name, const QMatrix4x3& value);
void setUniformValue(const char *name, const QMatrix4x4& value);
+ void setUniformValue(const char *name, const GLfloat value[2][2]);
+ void setUniformValue(const char *name, const GLfloat value[3][3]);
void setUniformValue(const char *name, const GLfloat value[4][4]);
void setUniformValue(const char *name, const QTransform& value);
diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.cpp b/src/plugins/imageformats/jpeg/qjpeghandler.cpp
index aa239ec..11608ef 100644
--- a/src/plugins/imageformats/jpeg/qjpeghandler.cpp
+++ b/src/plugins/imageformats/jpeg/qjpeghandler.cpp
@@ -90,9 +90,6 @@ public:
QImage scale();
-protected:
- int scaledWidth(void) const;
-
private:
QImageSmoothScalerPrivate *d;
virtual QRgb *scanLine(const int line = 0, const QImage *src = 0);
@@ -140,11 +137,6 @@ void QImageSmoothScalerPrivate::setup(const int srcWidth, const int srcHeight,
hasAlpha = hasAlphaChannel;
}
-int QImageSmoothScaler::scaledWidth() const
-{
- return d->cols;
-}
-
QImageSmoothScaler::~QImageSmoothScaler()
{
delete d;
@@ -443,20 +435,18 @@ QImage QImageSmoothScaler::scale()
class jpegSmoothScaler : public QImageSmoothScaler
{
public:
- jpegSmoothScaler(struct jpeg_decompress_struct *info, int dstWidth, int dstHeight)
- : QImageSmoothScaler(info->output_width, info->output_height, dstWidth, dstHeight)
+ jpegSmoothScaler(struct jpeg_decompress_struct *info, const QSize& dstSize, const QRect& clipRect)
+ : QImageSmoothScaler(clipRect.width(), clipRect.height(),
+ dstSize.width(), dstSize.height())
{
- cinfo = info;
- cols24Bit = scaledWidth() * 3;
-
- cacheHeight = 1;
- imageCache = QImage( info->output_width, cacheHeight, QImage::Format_RGB32 );
+ cinfo = info;
+ clip = clipRect;
+ imageCache = QImage(info->output_width, 1, QImage::Format_RGB32);
}
private:
- int cols24Bit;
+ QRect clip;
QImage imageCache;
- int cacheHeight;
struct jpeg_decompress_struct *cinfo;
QRgb *scanLine(const int line = 0, const QImage *src = 0)
@@ -468,33 +458,42 @@ private:
Q_UNUSED(src);
uchar* data = imageCache.bits();
+
+ // Read ahead if we haven't reached the first clipped scanline yet.
+ while (int(cinfo->output_scanline) < clip.y() &&
+ cinfo->output_scanline < cinfo->output_height)
+ jpeg_read_scanlines(cinfo, &data, 1);
+
+ // Read the next scanline. We assume that "line"
+ // will never be >= clip.height().
jpeg_read_scanlines(cinfo, &data, 1);
- out = (QRgb*)imageCache.scanLine(0);
+ if (cinfo->output_scanline == cinfo->output_height)
+ jpeg_finish_decompress(cinfo);
+
+ out = ((QRgb*)data) + clip.x();
//
// The smooth scale algorithm only works on 32-bit images;
// convert from (8|24) bits to 32.
//
if (cinfo->output_components == 1) {
- in = (uchar*)out + scaledWidth();
- for (uint i = scaledWidth(); i--; ) {
- in--;
+ in = data + clip.right();
+ for (int i = clip.width(); i--; ) {
out[i] = qRgb(*in, *in, *in);
+ in--;
}
- } else if (cinfo->out_color_space == JCS_CMYK) {
- int cols32Bit = scaledWidth() * 4;
- in = (uchar*)out + cols32Bit;
- for (uint i = scaledWidth(); i--; ) {
- in -= 4;
- int k = in[3];
- out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
- //out[i] = qRgb(in[0], in[1], in[2]);
- }
- } else {
- in = (uchar*)out + cols24Bit;
- for (uint i = scaledWidth(); i--; ) {
- in -= 3;
+ } else if (cinfo->out_color_space == JCS_CMYK) {
+ in = data + clip.right() * 4;
+ for (int i = clip.width(); i--; ) {
+ int k = in[3];
+ out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
+ in -= 4;
+ }
+ } else {
+ in = data + clip.right() * 3;
+ for (int i = clip.width(); i--; ) {
out[i] = qRgb(in[0], in[1], in[2]);
+ in -= 3;
}
}
@@ -693,7 +692,7 @@ static bool read_jpeg_format(QIODevice *device, QImage::Format &format)
}
static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
- bool dummy = false)
+ const QSize& size)
{
QImage::Format format;
switch (info->output_components) {
@@ -708,13 +707,8 @@ static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
return false; // unsupported format
}
- const QSize size(info->output_width, info->output_height);
if (dest->size() != size || dest->format() != format) {
- static uchar dummyImage[1];
- if (dummy) // Create QImage but don't read the pixels
- *dest = QImage(dummyImage, size.width(), size.height(), format);
- else
- *dest = QImage(size, format);
+ *dest = QImage(size, format);
if (format == QImage::Format_Indexed8) {
dest->setColorCount(256);
@@ -727,12 +721,9 @@ static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
}
static bool read_jpeg_image(QIODevice *device, QImage *outImage,
- QSize scaledSize, int inQuality )
+ QSize scaledSize, QRect scaledClipRect,
+ QRect clipRect, int inQuality )
{
-#ifdef QT_NO_IMAGE_SMOOTHSCALE
- Q_UNUSED( scaledSize );
-#endif
-
struct jpeg_decompress_struct cinfo;
struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
@@ -757,11 +748,53 @@ static bool read_jpeg_image(QIODevice *device, QImage *outImage,
if (quality < 0)
quality = 75;
-#ifndef QT_NO_IMAGE_SMOOTHSCALE
- // If high quality not required, shrink image during decompression
- if (scaledSize.isValid() && !scaledSize.isEmpty() && quality < HIGH_QUALITY_THRESHOLD) {
- cinfo.scale_denom = qMin(cinfo.image_width / scaledSize.width(),
- cinfo.image_width / scaledSize.height());
+ // If possible, merge the scaledClipRect into either scaledSize
+ // or clipRect to avoid doing a separate scaled clipping pass.
+ // Best results are achieved by clipping before scaling, not after.
+ if (!scaledClipRect.isEmpty()) {
+ if (scaledSize.isEmpty() && clipRect.isEmpty()) {
+ // No clipping or scaling before final clip.
+ clipRect = scaledClipRect;
+ scaledClipRect = QRect();
+ } else if (scaledSize.isEmpty()) {
+ // Clipping, but no scaling: combine the clip regions.
+ scaledClipRect.translate(clipRect.topLeft());
+ clipRect = scaledClipRect.intersected(clipRect);
+ scaledClipRect = QRect();
+ } else if (clipRect.isEmpty()) {
+ // No clipping, but scaling: if we can map back to an
+ // integer pixel boundary, then clip before scaling.
+ if ((cinfo.image_width % scaledSize.width()) == 0 &&
+ (cinfo.image_height % scaledSize.height()) == 0) {
+ int x = scaledClipRect.x() * cinfo.image_width /
+ scaledSize.width();
+ int y = scaledClipRect.y() * cinfo.image_height /
+ scaledSize.height();
+ int width = (scaledClipRect.right() + 1) *
+ cinfo.image_width / scaledSize.width() - x;
+ int height = (scaledClipRect.bottom() + 1) *
+ cinfo.image_height / scaledSize.height() - y;
+ clipRect = QRect(x, y, width, height);
+ scaledSize = scaledClipRect.size();
+ scaledClipRect = QRect();
+ }
+ } else {
+ // Clipping and scaling: too difficult to figure out,
+ // and not a likely use case, so do it the long way.
+ }
+ }
+
+ // Determine the scale factor to pass to libjpeg for quick downscaling.
+ if (!scaledSize.isEmpty()) {
+ if (clipRect.isEmpty()) {
+ cinfo.scale_denom =
+ qMin(cinfo.image_width / scaledSize.width(),
+ cinfo.image_height / scaledSize.height());
+ } else {
+ cinfo.scale_denom =
+ qMin(clipRect.width() / scaledSize.width(),
+ clipRect.height() / scaledSize.height());
+ }
if (cinfo.scale_denom < 2) {
cinfo.scale_denom = 1;
} else if (cinfo.scale_denom < 4) {
@@ -772,9 +805,19 @@ static bool read_jpeg_image(QIODevice *device, QImage *outImage,
cinfo.scale_denom = 8;
}
cinfo.scale_num = 1;
+ if (!clipRect.isEmpty()) {
+ // Correct the scale factor so that we clip accurately.
+ // It is recommended that the clip rectangle be aligned
+ // on an 8-pixel boundary for best performance.
+ while (cinfo.scale_denom > 1 &&
+ ((clipRect.x() % cinfo.scale_denom) != 0 ||
+ (clipRect.y() % cinfo.scale_denom) != 0 ||
+ (clipRect.width() % cinfo.scale_denom) != 0 ||
+ (clipRect.height() % cinfo.scale_denom) != 0)) {
+ cinfo.scale_denom /= 2;
+ }
+ }
}
-#endif
-
// If high quality not required, use fast decompression
if( quality < HIGH_QUALITY_THRESHOLD ) {
@@ -782,54 +825,102 @@ static bool read_jpeg_image(QIODevice *device, QImage *outImage,
cinfo.do_fancy_upsampling = FALSE;
}
+ (void) jpeg_calc_output_dimensions(&cinfo);
- (void) jpeg_start_decompress(&cinfo);
+ // Determine the clip region to extract.
+ QRect imageRect(0, 0, cinfo.output_width, cinfo.output_height);
+ QRect clip;
+ if (clipRect.isEmpty()) {
+ clip = imageRect;
+ } else if (cinfo.scale_denom == 1) {
+ clip = clipRect.intersected(imageRect);
+ } else {
+ // The scale factor was corrected above to ensure that
+ // we don't miss pixels when we scale the clip rectangle.
+ clip = QRect(clipRect.x() / int(cinfo.scale_denom),
+ clipRect.y() / int(cinfo.scale_denom),
+ clipRect.width() / int(cinfo.scale_denom),
+ clipRect.height() / int(cinfo.scale_denom));
+ clip = clip.intersected(imageRect);
+ }
#ifndef QT_NO_IMAGE_SMOOTHSCALE
- if (scaledSize.isValid() && scaledSize != QSize(cinfo.output_width, cinfo.output_height)
+ if (scaledSize.isValid() && scaledSize != clip.size()
&& quality >= HIGH_QUALITY_THRESHOLD) {
- jpegSmoothScaler scaler(&cinfo, scaledSize.width(), scaledSize.height());
+ (void) jpeg_start_decompress(&cinfo);
+
+ jpegSmoothScaler scaler(&cinfo, scaledSize, clip);
*outImage = scaler.scale();
} else
#endif
{
- if (!ensureValidImage(outImage, &cinfo))
+ // Allocate memory for the clipped QImage.
+ if (!ensureValidImage(outImage, &cinfo, clip.size()))
longjmp(jerr.setjmp_buffer, 1);
- uchar* data = outImage->bits();
- int bpl = outImage->bytesPerLine();
- while (cinfo.output_scanline < cinfo.output_height) {
- uchar *d = data + cinfo.output_scanline * bpl;
- (void) jpeg_read_scanlines(&cinfo,
- &d,
- 1);
- }
- (void) jpeg_finish_decompress(&cinfo);
-
- if (cinfo.output_components == 3) {
- // Expand 24->32 bpp.
- for (uint j=0; j<cinfo.output_height; j++) {
- uchar *in = outImage->scanLine(j) + cinfo.output_width * 3;
- QRgb *out = (QRgb*)outImage->scanLine(j);
-
- for (uint i=cinfo.output_width; i--;) {
- in-=3;
- out[i] = qRgb(in[0], in[1], in[2]);
+ // Avoid memcpy() overhead if grayscale with no clipping.
+ bool quickGray = (cinfo.output_components == 1 &&
+ clip == imageRect);
+ if (!quickGray) {
+ // Ask the jpeg library to allocate a temporary row.
+ // The library will automatically delete it for us later.
+ // The libjpeg docs say we should do this before calling
+ // jpeg_start_decompress(). We can't use "new" here
+ // because we are inside the setjmp() block and an error
+ // in the jpeg input stream would cause a memory leak.
+ JSAMPARRAY rows = (cinfo.mem->alloc_sarray)
+ ((j_common_ptr)&cinfo, JPOOL_IMAGE,
+ cinfo.output_width * cinfo.output_components, 1);
+
+ (void) jpeg_start_decompress(&cinfo);
+
+ while (cinfo.output_scanline < cinfo.output_height) {
+ int y = int(cinfo.output_scanline) - clip.y();
+ if (y >= clip.height())
+ break; // We've read the entire clip region, so abort.
+
+ (void) jpeg_read_scanlines(&cinfo, rows, 1);
+
+ if (y < 0)
+ continue; // Haven't reached the starting line yet.
+
+ if (cinfo.output_components == 3) {
+ // Expand 24->32 bpp.
+ uchar *in = rows[0] + clip.x() * 3;
+ QRgb *out = (QRgb*)outImage->scanLine(y);
+ for (int i = 0; i < clip.width(); ++i) {
+ *out++ = qRgb(in[0], in[1], in[2]);
+ in += 3;
+ }
+ } else if (cinfo.out_color_space == JCS_CMYK) {
+ // Convert CMYK->RGB.
+ uchar *in = rows[0] + clip.x() * 4;
+ QRgb *out = (QRgb*)outImage->scanLine(y);
+ for (int i = 0; i < clip.width(); ++i) {
+ int k = in[3];
+ *out++ = qRgb(k * in[0] / 255, k * in[1] / 255,
+ k * in[2] / 255);
+ in += 4;
+ }
+ } else if (cinfo.output_components == 1) {
+ // Grayscale.
+ memcpy(outImage->scanLine(y),
+ rows[0] + clip.x(), clip.width());
}
}
- } else if (cinfo.out_color_space == JCS_CMYK) {
- for (uint j = 0; j < cinfo.output_height; ++j) {
- uchar *in = outImage->scanLine(j) + cinfo.output_width * 4;
- QRgb *out = (QRgb*)outImage->scanLine(j);
-
- for (uint i = cinfo.output_width; i--; ) {
- in-=4;
- int k = in[3];
- out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
- }
+ } else {
+ // Load unclipped grayscale data directly into the QImage.
+ (void) jpeg_start_decompress(&cinfo);
+ while (cinfo.output_scanline < cinfo.output_height) {
+ uchar *row = outImage->scanLine(cinfo.output_scanline);
+ (void) jpeg_read_scanlines(&cinfo, &row, 1);
}
}
+
+ if (cinfo.output_scanline == cinfo.output_height)
+ (void) jpeg_finish_decompress(&cinfo);
+
if (cinfo.density_unit == 1) {
outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54));
outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54));
@@ -838,13 +929,15 @@ static bool read_jpeg_image(QIODevice *device, QImage *outImage,
outImage->setDotsPerMeterY(int(100. * cinfo.Y_density));
}
- if (scaledSize.isValid() && scaledSize != QSize(cinfo.output_width, cinfo.output_height))
+ if (scaledSize.isValid() && scaledSize != clip.size())
*outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::FastTransformation);
}
}
jpeg_destroy_decompress(&cinfo);
delete iod_src;
+ if (!scaledClipRect.isEmpty())
+ *outImage = outImage->copy(scaledClipRect);
return !outImage->isNull();
}
@@ -1102,7 +1195,7 @@ bool QJpegHandler::read(QImage *image)
{
if (!canRead())
return false;
- return read_jpeg_image(device(), image, scaledSize, quality);
+ return read_jpeg_image(device(), image, scaledSize, scaledClipRect, clipRect, quality);
}
bool QJpegHandler::write(const QImage &image)
@@ -1113,9 +1206,9 @@ bool QJpegHandler::write(const QImage &image)
bool QJpegHandler::supportsOption(ImageOption option) const
{
return option == Quality
-#ifndef QT_NO_IMAGE_SMOOTHSCALE
|| option == ScaledSize
-#endif
+ || option == ScaledClipRect
+ || option == ClipRect
|| option == Size
|| option == ImageFormat;
}
@@ -1124,10 +1217,12 @@ QVariant QJpegHandler::option(ImageOption option) const
{
if (option == Quality) {
return quality;
-#ifndef QT_NO_IMAGE_SMOOTHSCALE
} else if (option == ScaledSize) {
return scaledSize;
-#endif
+ } else if (option == ScaledClipRect) {
+ return scaledClipRect;
+ } else if (option == ClipRect) {
+ return clipRect;
} else if (option == Size) {
if (canRead() && !device()->isSequential()) {
qint64 pos = device()->pos();
@@ -1154,10 +1249,12 @@ void QJpegHandler::setOption(ImageOption option, const QVariant &value)
{
if (option == Quality)
quality = value.toInt();
-#ifndef QT_NO_IMAGE_SMOOTHSCALE
else if ( option == ScaledSize )
scaledSize = value.toSize();
-#endif
+ else if ( option == ScaledClipRect )
+ scaledClipRect = value.toRect();
+ else if ( option == ClipRect )
+ clipRect = value.toRect();
}
QByteArray QJpegHandler::name() const
diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.h b/src/plugins/imageformats/jpeg/qjpeghandler.h
index 0a14a88..6870cd6 100644
--- a/src/plugins/imageformats/jpeg/qjpeghandler.h
+++ b/src/plugins/imageformats/jpeg/qjpeghandler.h
@@ -44,6 +44,7 @@
#include <QtGui/qimageiohandler.h>
#include <QtCore/QSize>
+#include <QtCore/QRect>
QT_BEGIN_NAMESPACE
@@ -67,6 +68,8 @@ public:
private:
int quality;
QSize scaledSize;
+ QRect scaledClipRect;
+ QRect clipRect;
};
QT_END_NAMESPACE
diff --git a/tests/auto/qimagereader/tst_qimagereader.cpp b/tests/auto/qimagereader/tst_qimagereader.cpp
index 15b1c1c..630cc03 100644
--- a/tests/auto/qimagereader/tst_qimagereader.cpp
+++ b/tests/auto/qimagereader/tst_qimagereader.cpp
@@ -322,7 +322,15 @@ void tst_QImageReader::setScaledSize_data()
QTest::newRow("PPM: test") << "test.ppm" << QSize(10, 10) << QByteArray("ppm");
QTest::newRow("XBM: gnus") << "gnus" << QSize(200, 200) << QByteArray("xbm");
#ifdef QTEST_HAVE_JPEG
- QTest::newRow("JPEG: beavis") << "beavis" << QSize(200, 200) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis A") << "beavis" << QSize(200, 200) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis B") << "beavis" << QSize(175, 175) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis C") << "beavis" << QSize(100, 100) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis D") << "beavis" << QSize(100, 200) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis E") << "beavis" << QSize(200, 100) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis F") << "beavis" << QSize(87, 87) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis G") << "beavis" << QSize(50, 45) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis H") << "beavis" << QSize(43, 43) << QByteArray("jpeg");
+ QTest::newRow("JPEG: beavis I") << "beavis" << QSize(25, 25) << QByteArray("jpeg");
#endif // QTEST_HAVE_JPEG
#ifdef QTEST_HAVE_GIF
QTest::newRow("GIF: earth") << "earth" << QSize(200, 200) << QByteArray("gif");