flip Y in uploads to bottom-up textures (and add gm test)
Review URL: http://codereview.appspot.com/4756043/
git-svn-id: http://skia.googlecode.com/svn/trunk@1882 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 1587a9a..373356e 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -482,6 +482,13 @@
#endif
};
+namespace skiagm {
+static GrContext* gGrContext;
+GrContext* GetGr() {
+ return gGrContext;
+}
+}
+
int main(int argc, char * const argv[]) {
SkAutoGraphics ag;
@@ -533,10 +540,9 @@
maxH = SkMax32(size.height(), maxH);
}
// setup a GL context for drawing offscreen
- GrContext* context = NULL;
SkEGLContext eglContext;
if (eglContext.init(maxW, maxH)) {
- context = GrContext::CreateGLShaderContext();
+ gGrContext = GrContext::CreateGLShaderContext();
}
@@ -558,7 +564,7 @@
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
bool testSuccess = test_drawing(gm, gRec[i],
- writePath, readPath, diffPath, context,
+ writePath, readPath, diffPath, gGrContext,
&forwardRenderedBitmap);
overallSuccess &= testSuccess;
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
new file mode 100644
index 0000000..e1ad4b3
--- /dev/null
+++ b/gm/texdata.cpp
@@ -0,0 +1,139 @@
+#include "gm.h"
+#include "GrContext.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+
+namespace skiagm {
+
+extern GrContext* GetGr();
+
+static const int S = 200;
+
+class TexDataGM : public GM {
+public:
+ TexDataGM() {}
+
+protected:
+ virtual SkString onShortName() {
+ return SkString("texdata");
+ }
+
+ virtual SkISize onISize() {
+ return make_isize(2*S, 2*S);
+ }
+
+ void drawBG(SkCanvas* canvas) {
+ canvas->drawColor(0xff000000);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ drawBG(canvas);
+ SkDevice* device = canvas->getDevice();
+ GrRenderTarget* target = (GrRenderTarget*) device->accessRenderTarget();
+ GrContext* ctx = GetGr();
+ if (ctx && target) {
+ SkPMColor gTextureData[(2 * S) * (2 * S)];
+ static const int stride = 2 * S;
+ static const SkPMColor gray = SkPackARGB32(0x40, 0x40, 0x40, 0x40);
+ static const SkPMColor white = SkPackARGB32(0xff, 0xff, 0xff, 0xff);
+ static const SkPMColor red = SkPackARGB32(0x80, 0x80, 0x00, 0x00);
+ static const SkPMColor blue = SkPackARGB32(0x80, 0x00, 0x00, 0x80);
+ static const SkPMColor green = SkPackARGB32(0x80, 0x00, 0x80, 0x00);
+ static const SkPMColor black = SkPackARGB32(0x00, 0x00, 0x00, 0x00);
+ for (int i = 0; i < 2; ++i) {
+ int offset = 0;
+ // fill upper-left
+ for (int y = 0; y < S; ++y) {
+ for (int x = 0; x < S; ++x) {
+ gTextureData[offset + y * stride + x] = gray;
+ }
+ }
+ // fill upper-right
+ offset = S;
+ for (int y = 0; y < S; ++y) {
+ for (int x = 0; x < S; ++x) {
+ gTextureData[offset + y * stride + x] = white;
+ }
+ }
+ // fill lower left
+ offset = S * stride;
+ for (int y = 0; y < S; ++y) {
+ for (int x = 0; x < S; ++x) {
+ gTextureData[offset + y * stride + x] = black;
+ }
+ }
+ // fill lower right
+ offset = S * stride + S;
+ for (int y = 0; y < S; ++y) {
+ for (int x = 0; x < S; ++x) {
+ gTextureData[offset + y * stride + x] = gray;
+ }
+ }
+
+ GrTextureDesc desc;
+ desc.fAALevel = kNone_GrAALevel;
+ // use RT flag bit because in GL it makes the texture be bottom-up
+ desc.fFlags = i ? kRenderTarget_GrTextureFlagBit :
+ kNone_GrTextureFlags;
+ desc.fFormat = kRGBA_8888_GrPixelConfig;
+ desc.fWidth = 2 * S;
+ desc.fHeight = 2 * S;
+ GrTexture* texture =
+ ctx->createUncachedTexture(desc, gTextureData, 0);
+
+ if (!texture) {
+ return;
+ }
+ GrAutoUnref au(texture);
+
+ ctx->setClip(GrRect::MakeWH(2*S, 2*S));
+ ctx->setRenderTarget(target);
+
+ GrPaint paint;
+ paint.reset();
+ paint.fColor = 0xffffffff;
+ paint.fSrcBlendCoeff = kOne_BlendCoeff;
+ paint.fDstBlendCoeff = kISA_BlendCoeff;
+ GrMatrix vm;
+ if (i) {
+ vm.setRotate(90, S , S);
+ } else {
+ vm.reset();
+ }
+ ctx->setMatrix(vm);
+ GrMatrix tm;
+ tm = vm;
+ tm.postIDiv(2*S, 2*S);
+ paint.getTextureSampler(0)->setMatrix(tm);
+ paint.setTexture(0, texture);
+
+ ctx->drawRect(paint, GrRect::MakeWH(2*S, 2*S));
+
+ // now update the lower right of the texture in first pass
+ // or upper right in second pass
+ offset = 0;
+ for (int y = 0; y < S; ++y) {
+ for (int x = 0; x < S; ++x) {
+ gTextureData[offset + y * stride + x] =
+ ((x + y) % 2) ? (i ? green : red) : blue;
+ }
+ }
+ // BUG: uploadTextureData doesn't force a flush
+ ctx->flush();
+ texture->uploadTextureData(S, i ? 0 : S, S, S, gTextureData, 4 * stride);
+ ctx->drawRect(paint, GrRect::MakeWH(2*S, 2*S));
+ }
+ }
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new TexDataGM; }
+static GMRegistry reg(MyFactory);
+
+}
+
diff --git a/gpu/src/GrGLTexture.cpp b/gpu/src/GrGLTexture.cpp
index 207246b..bbca5ed 100644
--- a/gpu/src/GrGLTexture.cpp
+++ b/gpu/src/GrGLTexture.cpp
@@ -162,51 +162,65 @@
GPUGL->setSpareTextureUnit();
- // glCompressedTexSubImage2D doesn't support any formats
+ // ES2 glCompressedTexSubImage2D doesn't support any formats
// (at least without extensions)
GrAssert(fUploadFormat != GR_GL_PALETTE8_RGBA8);
// in case we need a temporary, trimmed copy of the src pixels
- SkAutoSMalloc<128 * 128> trimStorage;
+ SkAutoSMalloc<128 * 128> tempStorage;
+ if (!rowBytes) {
+ rowBytes = fUploadByteCount * width;
+ }
/*
- * check if our srcData has extra bytes past each row. If so, we need
- * to trim those off here, since GL doesn't let us pass the rowBytes as
- * a parameter to glTexImage2D
+ * check whether to allocate a temporary buffer for flipping y or
+ * because our srcData has extra bytes past each row. If so, we need
+ * to trim those off here, since GL ES doesn't let us specify
+ * GL_UNPACK_ROW_LENGTH.
*/
-
- if (GR_GL_SUPPORT_DESKTOP) {
+ bool restoreGLRowLength = false;
+ bool flipY = kBottomUp_Orientation == fOrientation;
+ if (GR_GL_SUPPORT_DESKTOP && !flipY) {
+ // can't use this for flipping, only non-neg values allowed. :(
if (srcData && rowBytes) {
GR_GL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH,
rowBytes / fUploadByteCount));
+ restoreGLRowLength = true;
}
} else {
size_t trimRowBytes = width * fUploadByteCount;
- if (srcData && (trimRowBytes < rowBytes)) {
+ if (srcData && (trimRowBytes < rowBytes || flipY)) {
// copy the data into our new storage, skipping the trailing bytes
size_t trimSize = height * trimRowBytes;
const char* src = (const char*)srcData;
- char* dst = (char*)trimStorage.realloc(trimSize);
+ if (flipY) {
+ src += (height - 1) * rowBytes;
+ }
+ char* dst = (char*)tempStorage.realloc(trimSize);
for (int y = 0; y < height; y++) {
memcpy(dst, src, trimRowBytes);
- src += rowBytes;
+ if (flipY) {
+ src -= rowBytes;
+ } else {
+ src += rowBytes;
+ }
dst += trimRowBytes;
}
- // now point srcData to our trimmed version
- srcData = trimStorage.get();
+ // now point srcData to our copied version
+ srcData = tempStorage.get();
}
}
- // If we need to update textures that are created upside down
- // then we have to modify this code to flip the srcData
- GrAssert(kTopDown_Orientation == fOrientation);
+ if (flipY) {
+ y = this->height() - (y + height);
+ }
GR_GL(BindTexture(GR_GL_TEXTURE_2D, fTexIDObj->id()));
GR_GL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, fUploadByteCount));
GR_GL(TexSubImage2D(GR_GL_TEXTURE_2D, 0, x, y, width, height,
fUploadFormat, fUploadType, srcData));
if (GR_GL_SUPPORT_DESKTOP) {
- if (srcData && rowBytes) {
+ if (restoreGLRowLength) {
GR_GL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH, 0));
}
}
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 1a9d7fe..09b337c 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -790,46 +790,60 @@
return return_null_texture();
}
+ // We keep GrRenderTargets in GL's normal orientation so that they
+ // can be drawn to by the outside world without the client having
+ // to render upside down.
+ glDesc.fOrientation = renderTarget ? GrGLTexture::kBottomUp_Orientation :
+ GrGLTexture::kTopDown_Orientation;
+
GrAssert(as_size_t(desc.fAALevel) < GR_ARRAY_COUNT(fAASamples));
GrGLint samples = fAASamples[desc.fAALevel];
if (kNone_MSFBO == fMSFBOType && desc.fAALevel != kNone_GrAALevel) {
GrPrintf("AA RT requested but not supported on this platform.");
}
- GR_GL(GenTextures(1, &glDesc.fTextureID));
- if (!glDesc.fTextureID) {
- return return_null_texture();
- }
-
glDesc.fUploadByteCount = GrBytesPerPixel(desc.fFormat);
// in case we need a temporary, trimmed copy of the src pixels
- SkAutoSMalloc<128 * 128> trimStorage;
+ SkAutoSMalloc<128 * 128> tempStorage;
+ if (!rowBytes) {
+ rowBytes = glDesc.fUploadByteCount * desc.fWidth;
+ }
/*
- * check if our srcData has extra bytes past each row. If so, we need
- * to trim those off here, since GL doesn't let us pass the rowBytes as
- * a parameter to glTexImage2D
+ * check whether to allocate a temporary buffer for flipping y or
+ * because our srcData has extra bytes past each row. If so, we need
+ * to trim those off here, since GL ES doesn't let us specify
+ * GL_UNPACK_ROW_LENGTH.
*/
- if (GR_GL_SUPPORT_DESKTOP) {
- if (srcData) {
+ bool flipY = GrGLTexture::kBottomUp_Orientation == glDesc.fOrientation;
+ if (GR_GL_SUPPORT_DESKTOP && !flipY) {
+ if (srcData && rowBytes != desc.fWidth * glDesc.fUploadByteCount) {
GR_GL(PixelStorei(GR_GL_UNPACK_ROW_LENGTH,
rowBytes / glDesc.fUploadByteCount));
}
} else {
size_t trimRowBytes = desc.fWidth * glDesc.fUploadByteCount;
- if (srcData && (trimRowBytes < rowBytes)) {
+ if (srcData && (trimRowBytes < rowBytes || flipY)) {
// copy the data into our new storage, skipping the trailing bytes
size_t trimSize = desc.fHeight * trimRowBytes;
const char* src = (const char*)srcData;
- char* dst = (char*)trimStorage.realloc(trimSize);
+ if (flipY) {
+ src += (desc.fHeight - 1) * rowBytes;
+ }
+ char* dst = (char*)tempStorage.realloc(trimSize);
for (int y = 0; y < desc.fHeight; y++) {
memcpy(dst, src, trimRowBytes);
- src += rowBytes;
+ if (flipY) {
+ src -= rowBytes;
+ } else {
+ src += rowBytes;
+ }
dst += trimRowBytes;
}
// now point srcData to our trimmed version
- srcData = trimStorage.get();
+ srcData = tempStorage.get();
+ rowBytes = trimRowBytes;
}
}
@@ -856,6 +870,11 @@
}
}
+ GR_GL(GenTextures(1, &glDesc.fTextureID));
+ if (!glDesc.fTextureID) {
+ return return_null_texture();
+ }
+
GR_GL(BindTexture(GR_GL_TEXTURE_2D, glDesc.fTextureID));
GR_GL(TexParameteri(GR_GL_TEXTURE_2D,
GR_GL_TEXTURE_MAG_FILTER,
@@ -901,36 +920,39 @@
SkAutoSMalloc<128*128> texels(glDesc.fUploadByteCount * maxTexels);
- uint32_t rowSize = desc.fWidth * glDesc.fUploadByteCount;
+ // rowBytes is actual stride between rows in srcData
+ // rowDataBytes is the actual amount of non-pad data in a row
+ // and the stride used for uploading extraH rows.
+ uint32_t rowDataBytes = desc.fWidth * glDesc.fUploadByteCount;
if (extraH) {
uint8_t* lastRowStart = (uint8_t*) srcData +
- (desc.fHeight - 1) * rowSize;
+ (desc.fHeight - 1) * rowBytes;
uint8_t* extraRowStart = (uint8_t*)texels.get();
for (int i = 0; i < extraH; ++i) {
- memcpy(extraRowStart, lastRowStart, rowSize);
- extraRowStart += rowSize;
+ memcpy(extraRowStart, lastRowStart, rowDataBytes);
+ extraRowStart += rowDataBytes;
}
GR_GL(TexSubImage2D(GR_GL_TEXTURE_2D, 0, 0, desc.fHeight, desc.fWidth,
extraH, glDesc.fUploadFormat, glDesc.fUploadType,
texels.get()));
}
if (extraW) {
- uint8_t* edgeTexel = (uint8_t*)srcData + rowSize - glDesc.fUploadByteCount;
+ uint8_t* edgeTexel = (uint8_t*)srcData + rowDataBytes - glDesc.fUploadByteCount;
uint8_t* extraTexel = (uint8_t*)texels.get();
for (int j = 0; j < desc.fHeight; ++j) {
for (int i = 0; i < extraW; ++i) {
memcpy(extraTexel, edgeTexel, glDesc.fUploadByteCount);
extraTexel += glDesc.fUploadByteCount;
}
- edgeTexel += rowSize;
+ edgeTexel += rowBytes;
}
GR_GL(TexSubImage2D(GR_GL_TEXTURE_2D, 0, desc.fWidth, 0, extraW,
desc.fHeight, glDesc.fUploadFormat,
glDesc.fUploadType, texels.get()));
}
if (extraW && extraH) {
- uint8_t* cornerTexel = (uint8_t*)srcData + desc.fHeight * rowSize
+ uint8_t* cornerTexel = (uint8_t*)srcData + desc.fHeight * rowBytes
- glDesc.fUploadByteCount;
uint8_t* extraTexel = (uint8_t*)texels.get();
for (int i = 0; i < extraW*extraH; ++i) {
@@ -950,7 +972,6 @@
}
}
- glDesc.fOrientation = GrGLTexture::kTopDown_Orientation;
GrGLRenderTarget::GLRenderTargetIDs rtIDs;
rtIDs.fStencilRenderbufferID = 0;
@@ -968,12 +989,6 @@
GrGLenum status;
GrGLint err;
- // If need have both RT flag and srcData we have
- // to invert the data before uploading because FBO
- // will be rendered bottom up
- GrAssert(NULL == srcData);
- glDesc.fOrientation = GrGLTexture::kBottomUp_Orientation;
-
GR_GL(GenFramebuffers(1, &rtIDs.fTexFBOID));
GrAssert(rtIDs.fTexFBOID);
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
index 1219995..4ecbb47 100644
--- a/gyp/gm.gyp
+++ b/gyp/gm.gyp
@@ -22,6 +22,7 @@
'../gm/shapes.cpp',
'../gm/strokerects.cpp',
'../gm/strokes.cpp',
+ '../gm/texdata.cpp',
'../gm/tilemodes.cpp',
'../gm/xfermodes.cpp',
'../gm/shadertext.cpp',
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 142748c..b9cafc7 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -30,6 +30,9 @@
class SkMetaData;
class SkRegion;
+// This is an opaque classes, not interpreted by skia
+class SkGpuRenderTarget;
+
class SK_API SkDevice : public SkRefCnt {
public:
// SkDevice();
@@ -135,10 +138,10 @@
virtual void lockPixels();
virtual void unlockPixels();
- /** Return the device's associated texture, or NULL. If returned, it may be
- drawn into another device
+ /**
+ * Return the device's associated gpu render target, or NULL.
*/
- virtual SkGpuTexture* accessTexture() { return NULL; }
+ virtual SkGpuRenderTarget* accessRenderTarget() { return NULL; }
/**
* Called with the correct matrix and clip before this device is drawn
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 2bea0bf..bddf24e 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -74,7 +74,9 @@
virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&,
const SkClipStack& clipStack);
- virtual SkGpuTexture* accessTexture() { return (SkGpuTexture*)fTexture; }
+ virtual SkGpuRenderTarget* accessRenderTarget() {
+ return (SkGpuRenderTarget*)fRenderTarget;
+ }
// overrides from SkDevice