1/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkImage_Gpu.h"
9#include "SkCanvas.h"
10#include "GrContext.h"
11#include "SkGpuDevice.h"
12
13SkImage_Gpu::SkImage_Gpu(int w, int h, SkAlphaType at, GrTexture* tex,
14                         int sampleCountForNewSurfaces, SkSurface::Budgeted budgeted)
15    : INHERITED(w, h, NULL)
16    , fTexture(SkRef(tex))
17    , fSampleCountForNewSurfaces(sampleCountForNewSurfaces)
18    , fAlphaType(at)
19    , fBudgeted(budgeted)
20    {}
21
22SkSurface* SkImage_Gpu::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) const {
23    GrTexture* tex = this->getTexture();
24    SkASSERT(tex);
25    GrContext* ctx = tex->getContext();
26    if (!ctx) {
27        // the texture may have been abandoned, so we have to check
28        return NULL;
29    }
30    // TODO: Change signature of onNewSurface to take a budgeted param.
31    const SkSurface::Budgeted budgeted = SkSurface::kNo_Budgeted;
32    return SkSurface::NewRenderTarget(ctx, budgeted, info, fSampleCountForNewSurfaces, &props);
33}
34
35extern void SkTextureImageApplyBudgetedDecision(SkImage* image) {
36    if (image->getTexture()) {
37        ((SkImage_Gpu*)image)->applyBudgetDecision();
38    }
39}
40
41SkShader* SkImage_Gpu::onNewShader(SkShader::TileMode tileX, SkShader::TileMode tileY,
42                                   const SkMatrix* localMatrix) const {
43    SkBitmap bm;
44    GrWrapTextureInBitmap(fTexture, this->width(), this->height(), this->isOpaque(), &bm);
45    return SkShader::CreateBitmapShader(bm, tileX, tileY, localMatrix);
46}
47
48bool SkImage_Gpu::getROPixels(SkBitmap* dst) const {
49    SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
50    if (!dst->tryAllocPixels(SkImageInfo::MakeN32(this->width(), this->height(), at))) {
51        return false;
52    }
53    if (!fTexture->readPixels(0, 0, dst->width(), dst->height(), kSkia8888_GrPixelConfig,
54                              dst->getPixels(), dst->rowBytes())) {
55        return false;
56    }
57    return true;
58}
59
60bool SkImage_Gpu::isOpaque() const {
61    return GrPixelConfigIsOpaque(fTexture->config());
62}
63
64static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
65    switch (info.colorType()) {
66        case kRGBA_8888_SkColorType:
67        case kBGRA_8888_SkColorType:
68            break;
69        default:
70            return; // nothing to do
71    }
72
73    // SkColor is not necesarily RGBA or BGRA, but it is one of them on little-endian,
74    // and in either case, the alpha-byte is always in the same place, so we can safely call
75    // SkPreMultiplyColor()
76    //
77    SkColor* row = (SkColor*)pixels;
78    for (int y = 0; y < info.height(); ++y) {
79        for (int x = 0; x < info.width(); ++x) {
80            row[x] = SkPreMultiplyColor(row[x]);
81        }
82    }
83}
84
85bool SkImage_Gpu::onReadPixels(const SkImageInfo& info, void* pixels, size_t rowBytes,
86                               int srcX, int srcY) const {
87    GrPixelConfig config = SkImageInfo2GrPixelConfig(info.colorType(), info.alphaType(),
88                                                     info.profileType());
89    uint32_t flags = 0;
90    if (kUnpremul_SkAlphaType == info.alphaType() && kPremul_SkAlphaType == fAlphaType) {
91        // let the GPU perform this transformation for us
92        flags = GrContext::kUnpremul_PixelOpsFlag;
93    }
94    if (!fTexture->readPixels(srcX, srcY, info.width(), info.height(), config,
95                              pixels, rowBytes, flags)) {
96        return false;
97    }
98    // do we have to manually fix-up the alpha channel?
99    //      src         dst
100    //      unpremul    premul      fix manually
101    //      premul      unpremul    done by kUnpremul_PixelOpsFlag
102    // all other combos need to change.
103    //
104    // Should this be handled by Ganesh? todo:?
105    //
106    if (kPremul_SkAlphaType == info.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
107        apply_premul(info, pixels, rowBytes);
108    }
109    return true;
110}
111
112///////////////////////////////////////////////////////////////////////////////////////////////////
113
114SkImage* SkImage::NewFromTexture(GrContext* ctx, const GrBackendTextureDesc& desc, SkAlphaType at) {
115    if (desc.fWidth <= 0 || desc.fHeight <= 0) {
116        return NULL;
117    }
118    SkAutoTUnref<GrTexture> tex(ctx->textureProvider()->wrapBackendTexture(desc));
119    if (!tex) {
120        return NULL;
121    }
122    const SkSurface::Budgeted budgeted = SkSurface::kNo_Budgeted;
123    return SkNEW_ARGS(SkImage_Gpu, (desc.fWidth, desc.fHeight, at, tex, 0, budgeted));
124}
125
126SkImage* SkImage::NewFromTextureCopy(GrContext* ctx, const GrBackendTextureDesc& srcDesc,
127                                     SkAlphaType at) {
128    const bool isBudgeted = true;
129    const SkSurface::Budgeted budgeted = SkSurface::kYes_Budgeted;
130
131    if (srcDesc.fWidth <= 0 || srcDesc.fHeight <= 0) {
132        return NULL;
133    }
134    SkAutoTUnref<GrTexture> src(ctx->textureProvider()->wrapBackendTexture(srcDesc));
135    if (!src) {
136        return NULL;
137    }
138
139    GrSurfaceDesc dstDesc;
140    // need to be a rendertarget for readpixels to work, instead of kNone_GrSurfaceFlags
141    dstDesc.fFlags = kRenderTarget_GrSurfaceFlag;
142    dstDesc.fOrigin = srcDesc.fOrigin;
143    dstDesc.fWidth = srcDesc.fWidth;
144    dstDesc.fHeight = srcDesc.fHeight;
145    dstDesc.fConfig = srcDesc.fConfig;
146    dstDesc.fSampleCnt = srcDesc.fSampleCnt;
147
148    SkAutoTUnref<GrTexture> dst(ctx->textureProvider()->createTexture(
149                                                                  dstDesc, isBudgeted, NULL, 0));
150    if (!dst) {
151        return NULL;
152    }
153
154    const SkIRect srcR = SkIRect::MakeWH(dstDesc.fWidth, dstDesc.fHeight);
155    const SkIPoint dstP = SkIPoint::Make(0, 0);
156    ctx->copySurface(dst, src, srcR, dstP, GrContext::kFlushWrites_PixelOp);
157
158    const int sampleCount = 0;  // todo: make this an explicit parameter to newSurface()?
159    return SkNEW_ARGS(SkImage_Gpu, (dstDesc.fWidth, dstDesc.fHeight, at, dst, sampleCount,
160                                    budgeted));
161}
162