static void test_clear(skiatest::Reporter* reporter, GrContext* context,
                       GrTexture* rectangleTexture) {
    if (rectangleTexture->asRenderTarget()) {
        sk_sp<GrDrawContext> dc(
                            context->drawContext(sk_ref_sp(rectangleTexture->asRenderTarget()),
                                                 nullptr));
        if (!dc) {
            ERRORF(reporter, "Could not get GrDrawContext for rectangle texture.");
            return;
        }

        // Clear the whole thing.
        GrColor color0 = GrColorPackRGBA(0xA, 0xB, 0xC, 0xD);
        dc->clear(nullptr, color0, false);

        int w = rectangleTexture->width();
        int h = rectangleTexture->height();
        int pixelCnt = w * h;
        SkAutoTMalloc<uint32_t> expectedPixels(pixelCnt);

        // The clear color is a GrColor, our readback is to kRGBA_8888, which may be different.
        uint32_t expectedColor0 = 0;
        uint8_t* expectedBytes0 = SkTCast<uint8_t*>(&expectedColor0);
        expectedBytes0[0] = GrColorUnpackR(color0);
        expectedBytes0[1] = GrColorUnpackG(color0);
        expectedBytes0[2] = GrColorUnpackB(color0);
        expectedBytes0[3] = GrColorUnpackA(color0);
        for (int i = 0; i < rectangleTexture->width() * rectangleTexture->height(); ++i) {
            expectedPixels.get()[i] = expectedColor0;
        }

        // Clear the the top to a different color.
        GrColor color1 = GrColorPackRGBA(0x1, 0x2, 0x3, 0x4);
        SkIRect rect = SkIRect::MakeWH(w, h/2);
        dc->clear(&rect, color1, false);

        uint32_t expectedColor1 = 0;
        uint8_t* expectedBytes1 = SkTCast<uint8_t*>(&expectedColor1);
        expectedBytes1[0] = GrColorUnpackR(color1);
        expectedBytes1[1] = GrColorUnpackG(color1);
        expectedBytes1[2] = GrColorUnpackB(color1);
        expectedBytes1[3] = GrColorUnpackA(color1);

        for (int y = 0; y < h/2; ++y) {
            for (int x = 0; x < w; ++x) {
                expectedPixels.get()[y * h + x] = expectedColor1;
            }
        }

        test_read_pixels(reporter, context, rectangleTexture, expectedPixels.get());
    }
}
Example #2
0
bool does_full_buffer_contain_correct_color(GrColor* buffer,
                                            GrColor clearColor,
                                            GrPixelConfig config,
                                            int width,
                                            int height) {
    GrColor matchColor;
    if (kRGBA_8888_GrPixelConfig == config) {
        matchColor = clearColor;
    } else if (kBGRA_8888_GrPixelConfig) {
        // Hack to flip the R, B componets in the GrColor so that the comparrison will work below
        matchColor = GrColorPackRGBA(GrColorUnpackB(clearColor),
                                     GrColorUnpackG(clearColor),
                                     GrColorUnpackR(clearColor),
                                     GrColorUnpackA(clearColor));
    } else {
        // currently only supporting rgba_8888 and bgra_8888
        return false;
    }

    for (int j = 0; j < height; ++j) {
        for (int i = 0; i < width; ++i) {
            if (buffer[j * width + i] != matchColor) {
                return false;
            }
        }
    }
    return true;
}
Example #3
0
static void test_clear(skiatest::Reporter* reporter, GrSurfaceContext* rectContext) {
    if (GrRenderTargetContext* rtc = rectContext->asRenderTargetContext()) {
        // Clear the whole thing.
        GrColor color0 = GrColorPackRGBA(0xA, 0xB, 0xC, 0xD);
        rtc->clear(nullptr, color0, GrRenderTargetContext::CanClearFullscreen::kNo);

        int w = rtc->width();
        int h = rtc->height();
        int pixelCnt = w * h;
        SkAutoTMalloc<uint32_t> expectedPixels(pixelCnt);

        // The clear color is a GrColor, our readback is to kRGBA_8888, which may be different.
        uint32_t expectedColor0 = 0;
        uint8_t* expectedBytes0 = SkTCast<uint8_t*>(&expectedColor0);
        expectedBytes0[0] = GrColorUnpackR(color0);
        expectedBytes0[1] = GrColorUnpackG(color0);
        expectedBytes0[2] = GrColorUnpackB(color0);
        expectedBytes0[3] = GrColorUnpackA(color0);
        for (int i = 0; i < rtc->width() * rtc->height(); ++i) {
            expectedPixels.get()[i] = expectedColor0;
        }

        // Clear the the top to a different color.
        GrColor color1 = GrColorPackRGBA(0x1, 0x2, 0x3, 0x4);
        SkIRect rect = SkIRect::MakeWH(w, h/2);
        rtc->clear(&rect, color1, GrRenderTargetContext::CanClearFullscreen::kNo);

        uint32_t expectedColor1 = 0;
        uint8_t* expectedBytes1 = SkTCast<uint8_t*>(&expectedColor1);
        expectedBytes1[0] = GrColorUnpackR(color1);
        expectedBytes1[1] = GrColorUnpackG(color1);
        expectedBytes1[2] = GrColorUnpackB(color1);
        expectedBytes1[3] = GrColorUnpackA(color1);

        for (int y = 0; y < h/2; ++y) {
            for (int x = 0; x < w; ++x) {
                expectedPixels.get()[y * h + x] = expectedColor1;
            }
        }

        test_read_pixels(reporter, rtc, expectedPixels.get(), "RectangleTexture-clear");
    }
}
 GrColorComponentFlags componentsWithValue(unsigned value) const {
     GrColorComponentFlags flags = kNone_GrColorComponentFlags;
     if ((kR_GrColorComponentFlag & fFlags) && value == GrColorUnpackR(fColor)) {
         flags |= kR_GrColorComponentFlag;
     }
     if ((kG_GrColorComponentFlag & fFlags) && value == GrColorUnpackG(fColor)) {
         flags |= kG_GrColorComponentFlag;
     }
     if ((kB_GrColorComponentFlag & fFlags) && value == GrColorUnpackB(fColor)) {
         flags |= kB_GrColorComponentFlag;
     }
     if ((kA_GrColorComponentFlag & fFlags) && value == GrColorUnpackA(fColor)) {
         flags |= kA_GrColorComponentFlag;
     }
     return flags;
 }
 void onSetData(const GrGLSLProgramDataManager& pdm, const GrProcessor& processor) override {
     GrColor color = processor.cast<GrConstColorProcessor>().color();
     // We use the "illegal" color value as an uninit sentinel. However, ut isn't inherently
     // illegal to use this processor with unpremul colors. So we correctly handle the case
     // when the "illegal" color is used but we will always upload it.
     if (GrColor_ILLEGAL == color || fPrevColor != color) {
         static const float scale = 1.f / 255.f;
         float floatColor[4] = {
             GrColorUnpackR(color) * scale,
             GrColorUnpackG(color) * scale,
             GrColorUnpackB(color) * scale,
             GrColorUnpackA(color) * scale,
         };
         pdm.set4fv(fColorUniform, 1, floatColor);
         fPrevColor = color;
     }
 }
void GrConstColorProcessor::onComputeInvariantOutput(GrInvariantOutput* inout) const {
    if (kIgnore_InputMode == fMode) {
        inout->setToOther(kRGBA_GrColorComponentFlags, fColor,
                          GrInvariantOutput::kWillNot_ReadInput);
    } else {
        GrColor r = GrColorUnpackR(fColor);
        bool colorIsSingleChannel = r == GrColorUnpackG(fColor) && r == GrColorUnpackB(fColor) &&
                                    r == GrColorUnpackA(fColor);
        if (kModulateRGBA_InputMode == fMode) {
            if (colorIsSingleChannel) {
                inout->mulByKnownSingleComponent(r);
            } else {
                inout->mulByKnownFourComponents(fColor);
            }
        } else {
            if (colorIsSingleChannel) {
                inout->mulAlphaByKnownSingleComponent(r);
            } else {
                inout->mulAlphaByKnownFourComponents(fColor);
            }
        }
    }
}
Example #7
0
bool GrTextureToYUVPlanes(GrTexture* texture, const SkISize sizes[3], void* const planes[3],
                          const size_t rowBytes[3], SkYUVColorSpace colorSpace) {
    if (GrContext* context = texture->getContext()) {
        // Depending on the relative sizes of the y, u, and v planes we may do 1 to 3 draws/
        // readbacks.
        sk_sp<GrDrawContext> yuvDrawContext;
        sk_sp<GrDrawContext> yDrawContext;
        sk_sp<GrDrawContext> uvDrawContext;
        sk_sp<GrDrawContext> uDrawContext;
        sk_sp<GrDrawContext> vDrawContext;

        GrPixelConfig singleChannelPixelConfig;
        if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
            singleChannelPixelConfig = kAlpha_8_GrPixelConfig;
        } else {
            singleChannelPixelConfig = kRGBA_8888_GrPixelConfig;
        }

        // We issue draw(s) to convert from RGBA to Y, U, and V. All three planes may have different
        // sizes however we optimize for two other cases - all planes are the same (1 draw to YUV),
        // and U and V are the same but Y differs (2 draws, one for Y, one for UV).
        if (sizes[0] == sizes[1] && sizes[1] == sizes[2]) {
            yuvDrawContext = context->newDrawContext(SkBackingFit::kApprox,
                                                     sizes[0].fWidth, sizes[0].fHeight,
                                                     kRGBA_8888_GrPixelConfig);
            if (!yuvDrawContext) {
                return false;
            }
        } else {
            yDrawContext = context->newDrawContext(SkBackingFit::kApprox,
                                                   sizes[0].fWidth, sizes[0].fHeight,
                                                   singleChannelPixelConfig);
            if (!yDrawContext) {
                return false;
            }
            if (sizes[1] == sizes[2]) {
                // TODO: Add support for GL_RG when available.
                uvDrawContext = context->newDrawContext(SkBackingFit::kApprox,
                                                        sizes[1].fWidth, sizes[1].fHeight,
                                                        kRGBA_8888_GrPixelConfig);
                if (!uvDrawContext) {
                    return false;
                }
            } else {
                uDrawContext = context->newDrawContext(SkBackingFit::kApprox,
                                                       sizes[1].fWidth, sizes[1].fHeight,
                                                       singleChannelPixelConfig);
                vDrawContext = context->newDrawContext(SkBackingFit::kApprox,
                                                       sizes[2].fWidth, sizes[2].fHeight,
                                                       singleChannelPixelConfig);
                if (!uDrawContext || !vDrawContext) {
                    return false;
                }
            }
        }

        // Do all the draws before any readback.
        if (yuvDrawContext) {
            if (!convert_texture(texture, yuvDrawContext.get(),
                                 sizes[0].fWidth, sizes[0].fHeight,
                                 colorSpace, GrYUVEffect::MakeRGBToYUV)) {
                return false;
            }
        } else {
            SkASSERT(yDrawContext);
            if (!convert_texture(texture, yDrawContext.get(),
                                 sizes[0].fWidth, sizes[0].fHeight,
                                 colorSpace, GrYUVEffect::MakeRGBToY)) {
                return false;
            }
            if (uvDrawContext) {
                if (!convert_texture(texture, uvDrawContext.get(),
                                     sizes[1].fWidth, sizes[1].fHeight,
                                     colorSpace,  GrYUVEffect::MakeRGBToUV)) {
                    return false;
                }
            } else {
                SkASSERT(uDrawContext && vDrawContext);
                if (!convert_texture(texture, uDrawContext.get(),
                                     sizes[1].fWidth, sizes[1].fHeight,
                                     colorSpace, GrYUVEffect::MakeRGBToU)) {
                    return false;
                }
                if (!convert_texture(texture, vDrawContext.get(),
                                     sizes[2].fWidth, sizes[2].fHeight,
                                     colorSpace, GrYUVEffect::MakeRGBToV)) {
                    return false;
                }
            }
        }

        if (yuvDrawContext) {
            SkASSERT(sizes[0] == sizes[1] && sizes[1] == sizes[2]);
            sk_sp<GrTexture> yuvTex(yuvDrawContext->asTexture());
            SkASSERT(yuvTex);
            SkISize yuvSize = sizes[0];
            // We have no kRGB_888 pixel format, so readback rgba and then copy three channels.
            SkAutoSTMalloc<128 * 128, uint32_t> tempYUV(yuvSize.fWidth * yuvSize.fHeight);
            if (!yuvTex->readPixels(0, 0, yuvSize.fWidth, yuvSize.fHeight,
                                    kRGBA_8888_GrPixelConfig, tempYUV.get(), 0)) {
                return false;
            }
            size_t yRowBytes = rowBytes[0] ? rowBytes[0] : yuvSize.fWidth;
            size_t uRowBytes = rowBytes[1] ? rowBytes[1] : yuvSize.fWidth;
            size_t vRowBytes = rowBytes[2] ? rowBytes[2] : yuvSize.fWidth;
            if (yRowBytes < (size_t)yuvSize.fWidth || uRowBytes < (size_t)yuvSize.fWidth ||
                vRowBytes < (size_t)yuvSize.fWidth) {
                return false;
            }
            for (int j = 0; j < yuvSize.fHeight; ++j) {
                for (int i = 0; i < yuvSize.fWidth; ++i) {
                    // These writes could surely be made more efficient.
                    uint32_t y = GrColorUnpackR(tempYUV.get()[j * yuvSize.fWidth + i]);
                    uint32_t u = GrColorUnpackG(tempYUV.get()[j * yuvSize.fWidth + i]);
                    uint32_t v = GrColorUnpackB(tempYUV.get()[j * yuvSize.fWidth + i]);
                    uint8_t* yLoc = ((uint8_t*)planes[0]) + j * yRowBytes + i;
                    uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i;
                    uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i;
                    *yLoc = y;
                    *uLoc = u;
                    *vLoc = v;
                }
            }
            return true;
        } else {
            SkASSERT(yDrawContext);
            sk_sp<GrTexture> yTex(yDrawContext->asTexture());
            SkASSERT(yTex);
            if (!yTex->readPixels(0, 0, sizes[0].fWidth, sizes[0].fHeight,
                                  kAlpha_8_GrPixelConfig, planes[0], rowBytes[0])) {
                return false;
            }
            if (uvDrawContext) {
                SkASSERT(sizes[1].fWidth == sizes[2].fWidth);
                sk_sp<GrTexture> uvTex(uvDrawContext->asTexture());
                SkASSERT(uvTex);
                SkISize uvSize = sizes[1];
                // We have no kRG_88 pixel format, so readback rgba and then copy two channels.
                SkAutoSTMalloc<128 * 128, uint32_t> tempUV(uvSize.fWidth * uvSize.fHeight);
                if (!uvTex->readPixels(0, 0, uvSize.fWidth, uvSize.fHeight,
                                       kRGBA_8888_GrPixelConfig, tempUV.get(), 0)) {
                    return false;
                }

                size_t uRowBytes = rowBytes[1] ? rowBytes[1] : uvSize.fWidth;
                size_t vRowBytes = rowBytes[2] ? rowBytes[2] : uvSize.fWidth;
                if (uRowBytes < (size_t)uvSize.fWidth || vRowBytes < (size_t)uvSize.fWidth) {
                    return false;
                }
                for (int j = 0; j < uvSize.fHeight; ++j) {
                    for (int i = 0; i < uvSize.fWidth; ++i) {
                        // These writes could surely be made more efficient.
                        uint32_t u = GrColorUnpackR(tempUV.get()[j * uvSize.fWidth + i]);
                        uint32_t v = GrColorUnpackG(tempUV.get()[j * uvSize.fWidth + i]);
                        uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i;
                        uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i;
                        *uLoc = u;
                        *vLoc = v;
                    }
                }
                return true;
            } else {
                SkASSERT(uDrawContext && vDrawContext);
                sk_sp<GrTexture> tex(uDrawContext->asTexture());
                SkASSERT(tex);
                if (!tex->readPixels(0, 0, sizes[1].fWidth, sizes[1].fHeight,
                                     kAlpha_8_GrPixelConfig, planes[1], rowBytes[1])) {
                    return false;
                }
                tex = vDrawContext->asTexture();
                SkASSERT(tex);
                if (!tex->readPixels(0, 0, sizes[2].fWidth, sizes[2].fHeight,
                                     kAlpha_8_GrPixelConfig, planes[2], rowBytes[2])) {
                    return false;
                }
                return true;
            }
        }
    }
    return false;
}
bool GrGpuGLFixed::flushGraphicsState(GrPrimitiveType type) {

    bool usingTextures[kNumStages];

    for (int s = 0; s < kNumStages; ++s) {
        usingTextures[s] = VertexUsesStage(s, fGeometrySrc.fVertexLayout);

        if (usingTextures[s] && fCurrDrawState.fSamplerStates[s].isGradient()) {
            unimpl("Fixed pipe doesn't support radial/sweep gradients");
            return false;
        }
    }

    if (GR_GL_SUPPORT_ES1) {
        if (BlendCoefReferencesConstant(fCurrDrawState.fSrcBlend) ||
            BlendCoefReferencesConstant(fCurrDrawState.fDstBlend)) {
            unimpl("ES1 doesn't support blend constant");
            return false;
        }
    }

    if (!flushGLStateCommon(type)) {
        return false;
    }

    if (fDirtyFlags.fRenderTargetChanged) {
        flushProjectionMatrix();
    }

    for (int s = 0; s < kNumStages; ++s) {
        bool wasUsingTexture = VertexUsesStage(s, fHWGeometryState.fVertexLayout);
        if (usingTextures[s] != wasUsingTexture) {
            setTextureUnit(s);
            if (usingTextures[s]) {
                GR_GL(Enable(GR_GL_TEXTURE_2D));
            } else {
                GR_GL(Disable(GR_GL_TEXTURE_2D));
            }
        }
    }

    uint32_t vertColor = (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit);
    uint32_t prevVertColor = (fHWGeometryState.fVertexLayout &
                              kColor_VertexLayoutBit);

    if (vertColor != prevVertColor) {
        if (vertColor) {
            GR_GL(ShadeModel(GR_GL_SMOOTH));
            // invalidate the immediate mode color
            fHWDrawState.fColor = GrColor_ILLEGAL;
        } else {
            GR_GL(ShadeModel(GR_GL_FLAT));
        }
    }


    if (!vertColor && fHWDrawState.fColor != fCurrDrawState.fColor) {
        GR_GL(Color4ub(GrColorUnpackR(fCurrDrawState.fColor),
                       GrColorUnpackG(fCurrDrawState.fColor),
                       GrColorUnpackB(fCurrDrawState.fColor),
                       GrColorUnpackA(fCurrDrawState.fColor)));
        fHWDrawState.fColor = fCurrDrawState.fColor;
    }

    // set texture environment, decide whether we are modulating by RGB or A.
    for (int s = 0; s < kNumStages; ++s) {
        if (usingTextures[s]) {
            GrGLTexture* texture = (GrGLTexture*)fCurrDrawState.fTextures[s];
            if (NULL != texture) {
                TextureEnvRGBOperands nextRGBOperand0 =
                    (GrPixelConfigIsAlphaOnly(texture->config())) ?
                        kAlpha_TextureEnvRGBOperand :
                        kColor_TextureEnvRGBOperand;
                if (fHWRGBOperand0[s] != nextRGBOperand0) {
                    setTextureUnit(s);
                    GR_GL(TexEnvi(GR_GL_TEXTURE_ENV,
                                  GR_GL_OPERAND0_RGB,
                                  (nextRGBOperand0==kAlpha_TextureEnvRGBOperand) ?
                                    GR_GL_SRC_ALPHA :
                                    GR_GL_SRC_COLOR));
                    fHWRGBOperand0[s] = nextRGBOperand0;
                }

                if (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
                    (fHWDrawState.fSamplerStates[s].getMatrix() !=
                     getSamplerMatrix(s))) {

                    GrMatrix texMat = getSamplerMatrix(s);
                    AdjustTextureMatrix(texture,
                                        GrSamplerState::kNormal_SampleMode,
                                        &texMat);
                    GrGpuMatrix glm;
                    glm.set(texMat);
                    setTextureUnit(s);
                    GR_GL(MatrixMode(GR_GL_TEXTURE));
                    GR_GL(LoadMatrixf(glm.fMat));
                    recordHWSamplerMatrix(s, getSamplerMatrix(s));
                }
            } else {
                GrAssert(!"Rendering with texture vert flag set but no bound texture");
                return false;
            }
        }
    }

    if (fHWDrawState.fViewMatrix != fCurrDrawState.fViewMatrix) {
        GrGpuMatrix glm;
        glm.set(fCurrDrawState.fViewMatrix);
        GR_GL(MatrixMode(GR_GL_MODELVIEW));
        GR_GL(LoadMatrixf(glm.fMat));
        fHWDrawState.fViewMatrix =
        fCurrDrawState.fViewMatrix;
    }
    resetDirtyFlags();
    return true;
}
bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
    if (!flushGLStateCommon(type)) {
        return false;
    }

    if (fDirtyFlags.fRenderTargetChanged) {
        // our coords are in pixel space and the GL matrices map to NDC
        // so if the viewport changed, our matrix is now wrong.
#if ATTRIBUTE_MATRIX
        fHWDrawState.fViewMatrix = GrMatrix::InvalidMatrix();
#else
        // we assume all shader matrices may be wrong after viewport changes
        fProgramCache->invalidateViewMatrices();
#endif
    }

    if (fGeometrySrc.fVertexLayout & kColor_VertexLayoutBit) {
        // invalidate the immediate mode color
        fHWDrawState.fColor = GrColor_ILLEGAL;
    } else {
        if (fHWDrawState.fColor != fCurrDrawState.fColor) {
            // OpenGL ES only supports the float varities of glVertexAttrib
            float c[] = {
                GrColorUnpackR(fCurrDrawState.fColor) / 255.f,
                GrColorUnpackG(fCurrDrawState.fColor) / 255.f,
                GrColorUnpackB(fCurrDrawState.fColor) / 255.f,
                GrColorUnpackA(fCurrDrawState.fColor) / 255.f
            };
            GR_GL(VertexAttrib4fv(COL_ATTR_LOCATION, c));
            fHWDrawState.fColor = fCurrDrawState.fColor;
        }
    }

    buildProgram(type);
    fProgramData = fProgramCache->getProgramData(fCurrentProgram, this);

    if (fHWProgramID != fProgramData->fProgramID) {
        GR_GL(UseProgram(fProgramData->fProgramID));
        fHWProgramID = fProgramData->fProgramID;
    }

    if (!fCurrentProgram.doGLSetup(type, fProgramData)) {
        return false;
    }

#if ATTRIBUTE_MATRIX
    GrMatrix& currViewMatrix = fHWDrawState.fViewMatrix;
#else
    GrMatrix& currViewMatrix = fProgramData->fViewMatrix;
#endif

    if (currViewMatrix != fCurrDrawState.fViewMatrix) {
        flushViewMatrix();
        currViewMatrix = fCurrDrawState.fViewMatrix;
    }

    for (int s = 0; s < kNumStages; ++s) {
        GrGLTexture* texture = (GrGLTexture*) fCurrDrawState.fTextures[s];
        if (NULL != texture) {
            if (-1 != fProgramData->fUniLocations.fStages[s].fTextureMatrixUni &&
                (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
                getHWSamplerMatrix(s) != getSamplerMatrix(s))) {
                flushTextureMatrix(s);
                recordHWSamplerMatrix(s, getSamplerMatrix(s));
            }
        }

        const GrSamplerState& sampler = fCurrDrawState.fSamplerStates[s];
        if (-1 != fProgramData->fUniLocations.fStages[s].fRadial2Uni &&
            (fProgramData->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
             fProgramData->fRadial2Radius0[s]  != sampler.getRadial2Radius0()  ||
             fProgramData->fRadial2PosRoot[s]  != sampler.isRadial2PosRoot())) {

            flushRadial2(s);

            fProgramData->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
            fProgramData->fRadial2Radius0[s]  = sampler.getRadial2Radius0();
            fProgramData->fRadial2PosRoot[s]  = sampler.isRadial2PosRoot();
        }
    }
    resetDirtyFlags();
    return true;
}
bool GrTextureToYUVPlanes(GrTexture* texture, const SkISize sizes[3], void* const planes[3],
                          const size_t rowBytes[3], SkYUVColorSpace colorSpace) {
    if (GrContext* context = texture->getContext()) {
        // Depending on the relative sizes of the y, u, and v planes we may do 1 to 3 draws/
        // readbacks.
        SkAutoTUnref<GrTexture> yuvTex;
        SkAutoTUnref<GrTexture> yTex;
        SkAutoTUnref<GrTexture> uvTex;
        SkAutoTUnref<GrTexture> uTex;
        SkAutoTUnref<GrTexture> vTex;

        GrPixelConfig singleChannelPixelConfig;
        if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
            singleChannelPixelConfig = kAlpha_8_GrPixelConfig;
        } else {
            singleChannelPixelConfig = kRGBA_8888_GrPixelConfig;
        }

        // We issue draw(s) to convert from RGBA to Y, U, and V. All three planes may have different
        // sizes however we optimize for two other cases - all planes are the same (1 draw to YUV),
        // and U and V are the same but Y differs (2 draws, one for Y, one for UV).
        if (sizes[0] == sizes[1] && sizes[1] == sizes[2]) {
            GrSurfaceDesc yuvDesc;
            yuvDesc.fConfig = kRGBA_8888_GrPixelConfig;
            yuvDesc.fFlags = kRenderTarget_GrSurfaceFlag;
            yuvDesc.fWidth = sizes[0].fWidth;
            yuvDesc.fHeight = sizes[0].fHeight;
            yuvTex.reset(context->textureProvider()->createApproxTexture(yuvDesc));
            if (!yuvTex) {
                return false;
            }
        } else {
            GrSurfaceDesc yDesc;
            yDesc.fConfig = singleChannelPixelConfig;
            yDesc.fFlags = kRenderTarget_GrSurfaceFlag;
            yDesc.fWidth = sizes[0].fWidth;
            yDesc.fHeight = sizes[0].fHeight;
            yTex.reset(context->textureProvider()->createApproxTexture(yDesc));
            if (!yTex) {
                return false;
            }
            if (sizes[1] == sizes[2]) {
                GrSurfaceDesc uvDesc;
                // TODO: Add support for GL_RG when available.
                uvDesc.fConfig = kRGBA_8888_GrPixelConfig;
                uvDesc.fFlags = kRenderTarget_GrSurfaceFlag;
                uvDesc.fWidth = sizes[1].fWidth;
                uvDesc.fHeight = sizes[1].fHeight;
                uvTex.reset(context->textureProvider()->createApproxTexture(uvDesc));
                if (!uvTex) {
                    return false;
                }
            } else {
                GrSurfaceDesc uvDesc;
                uvDesc.fConfig = singleChannelPixelConfig;
                uvDesc.fFlags = kRenderTarget_GrSurfaceFlag;
                uvDesc.fWidth = sizes[1].fWidth;
                uvDesc.fHeight = sizes[1].fHeight;
                uTex.reset(context->textureProvider()->createApproxTexture(uvDesc));
                uvDesc.fWidth = sizes[2].fWidth;
                uvDesc.fHeight = sizes[2].fHeight;
                vTex.reset(context->textureProvider()->createApproxTexture(uvDesc));
                if (!uTex || !vTex) {
                    return false;
                }
            }
        }

        // Do all the draws before any readback.
        if (yuvTex) {
            SkAutoTUnref<GrDrawContext> dc(context->drawContext(yuvTex->asRenderTarget()));
            if (!dc) {
                return false;
            }
            if (!convert_texture(texture, dc, sizes[0].fWidth, sizes[0].fHeight, colorSpace,
                                 GrYUVEffect::CreateRGBToYUV)) {
                return false;
            }

        } else {
            SkASSERT(yTex);
            SkAutoTUnref<GrDrawContext> dc(context->drawContext(yTex->asRenderTarget()));
            if (!dc) {
                return false;
            }
            if (!convert_texture(texture, dc, sizes[0].fWidth, sizes[0].fHeight, colorSpace,
                                 GrYUVEffect::CreateRGBToY)) {
                return false;
            }
            if (uvTex) {
                dc.reset(context->drawContext(uvTex->asRenderTarget()));
                if (!dc) {
                    return false;
                }
                if (!convert_texture(texture, dc, sizes[1].fWidth, sizes[1].fHeight,
                                     colorSpace,  GrYUVEffect::CreateRGBToUV)) {
                    return false;
                }
            } else {
                SkASSERT(uTex && vTex);
                dc.reset(context->drawContext(uTex->asRenderTarget()));
                if (!dc) {
                    return false;
                }
                if (!convert_texture(texture, dc, sizes[1].fWidth, sizes[1].fHeight,
                                     colorSpace, GrYUVEffect::CreateRGBToU)) {
                    return false;
                }
                dc.reset(context->drawContext(vTex->asRenderTarget()));
                if (!dc) {
                    return false;
                }
                if (!convert_texture(texture, dc, sizes[2].fWidth, sizes[2].fHeight,
                                     colorSpace, GrYUVEffect::CreateRGBToV)) {
                    return false;
                }
            }
        }

        if (yuvTex) {
            SkASSERT(sizes[0] == sizes[1] && sizes[1] == sizes[2]);
            SkISize yuvSize = sizes[0];
            // We have no kRGB_888 pixel format, so readback rgba and then copy three channels.
            SkAutoSTMalloc<128 * 128, uint32_t> tempYUV(yuvSize.fWidth * yuvSize.fHeight);
            if (!yuvTex->readPixels(0, 0, yuvSize.fWidth, yuvSize.fHeight,
                                    kRGBA_8888_GrPixelConfig, tempYUV.get(), 0)) {
                return false;
            }
            size_t yRowBytes = rowBytes[0] ? rowBytes[0] : yuvSize.fWidth;
            size_t uRowBytes = rowBytes[1] ? rowBytes[1] : yuvSize.fWidth;
            size_t vRowBytes = rowBytes[2] ? rowBytes[2] : yuvSize.fWidth;
            if (yRowBytes < (size_t)yuvSize.fWidth || uRowBytes < (size_t)yuvSize.fWidth ||
                vRowBytes < (size_t)yuvSize.fWidth) {
                return false;
            }
            for (int j = 0; j < yuvSize.fHeight; ++j) {
                for (int i = 0; i < yuvSize.fWidth; ++i) {
                    // These writes could surely be made more efficient.
                    uint32_t y = GrColorUnpackR(tempYUV.get()[j * yuvSize.fWidth + i]);
                    uint32_t u = GrColorUnpackG(tempYUV.get()[j * yuvSize.fWidth + i]);
                    uint32_t v = GrColorUnpackB(tempYUV.get()[j * yuvSize.fWidth + i]);
                    uint8_t* yLoc = ((uint8_t*)planes[0]) + j * yRowBytes + i;
                    uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i;
                    uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i;
                    *yLoc = y;
                    *uLoc = u;
                    *vLoc = v;
                }
            }
            return true;
        } else {
            SkASSERT(yTex);
            if (!yTex->readPixels(0, 0, sizes[0].fWidth, sizes[0].fHeight,
                                  kAlpha_8_GrPixelConfig, planes[0], rowBytes[0])) {
                return false;
            }
            if (uvTex) {
                SkASSERT(sizes[1].fWidth == sizes[2].fWidth);
                SkISize uvSize = sizes[1];
                // We have no kRG_88 pixel format, so readback rgba and then copy two channels.
                SkAutoSTMalloc<128 * 128, uint32_t> tempUV(uvSize.fWidth * uvSize.fHeight);
                if (!uvTex->readPixels(0, 0, uvSize.fWidth, uvSize.fHeight,
                                       kRGBA_8888_GrPixelConfig, tempUV.get(), 0)) {
                    return false;
                }

                size_t uRowBytes = rowBytes[1] ? rowBytes[1] : uvSize.fWidth;
                size_t vRowBytes = rowBytes[2] ? rowBytes[2] : uvSize.fWidth;
                if (uRowBytes < (size_t)uvSize.fWidth || vRowBytes < (size_t)uvSize.fWidth) {
                    return false;
                }
                for (int j = 0; j < uvSize.fHeight; ++j) {
                    for (int i = 0; i < uvSize.fWidth; ++i) {
                        // These writes could surely be made more efficient.
                        uint32_t u = GrColorUnpackR(tempUV.get()[j * uvSize.fWidth + i]);
                        uint32_t v = GrColorUnpackG(tempUV.get()[j * uvSize.fWidth + i]);
                        uint8_t* uLoc = ((uint8_t*)planes[1]) + j * uRowBytes + i;
                        uint8_t* vLoc = ((uint8_t*)planes[2]) + j * vRowBytes + i;
                        *uLoc = u;
                        *vLoc = v;
                    }
                }
                return true;
            } else {
                SkASSERT(uTex && vTex);
                if (!uTex->readPixels(0, 0, sizes[1].fWidth, sizes[1].fHeight,
                                      kAlpha_8_GrPixelConfig, planes[1], rowBytes[1])) {
                    return false;
                }
                if (!vTex->readPixels(0, 0, sizes[2].fWidth, sizes[2].fHeight,
                                      kAlpha_8_GrPixelConfig, planes[2], rowBytes[2])) {
                    return false;
                }
                return true;
            }
        }
    }
    return false;
}