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 
SkImage_Gpu(int w,int h,SkAlphaType at,GrTexture * tex,int sampleCountForNewSurfaces,SkSurface::Budgeted budgeted)13 SkImage_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 
onNewSurface(const SkImageInfo & info,const SkSurfaceProps & props) const22 SkSurface* 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 
SkTextureImageApplyBudgetedDecision(SkImage * image)35 extern void SkTextureImageApplyBudgetedDecision(SkImage* image) {
36     if (image->getTexture()) {
37         ((SkImage_Gpu*)image)->applyBudgetDecision();
38     }
39 }
40 
onNewShader(SkShader::TileMode tileX,SkShader::TileMode tileY,const SkMatrix * localMatrix) const41 SkShader* 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 
getROPixels(SkBitmap * dst) const48 bool 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 
isOpaque() const60 bool SkImage_Gpu::isOpaque() const {
61     return GrPixelConfigIsOpaque(fTexture->config());
62 }
63 
apply_premul(const SkImageInfo & info,void * pixels,size_t rowBytes)64 static 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 
onReadPixels(const SkImageInfo & info,void * pixels,size_t rowBytes,int srcX,int srcY) const85 bool 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 
NewFromTexture(GrContext * ctx,const GrBackendTextureDesc & desc,SkAlphaType at)114 SkImage* 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 
NewFromTextureCopy(GrContext * ctx,const GrBackendTextureDesc & srcDesc,SkAlphaType at)126 SkImage* 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