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 "SkBitmap.h"
9 #include "SkBitmapCache.h"
10 #include "SkCanvas.h"
11 #include "SkData.h"
12 #include "SkImageEncoder.h"
13 #include "SkImageGenerator.h"
14 #include "SkImagePriv.h"
15 #include "SkImageShader.h"
16 #include "SkImage_Base.h"
17 #include "SkNextID.h"
18 #include "SkPixelRef.h"
19 #include "SkPixelSerializer.h"
20 #include "SkReadPixelsRec.h"
21 #include "SkString.h"
22 #include "SkSurface.h"
23
24 #if SK_SUPPORT_GPU
25 #include "GrTexture.h"
26 #include "GrContext.h"
27 #include "SkImage_Gpu.h"
28 #endif
29
SkImage(int width,int height,uint32_t uniqueID)30 SkImage::SkImage(int width, int height, uint32_t uniqueID)
31 : fWidth(width)
32 , fHeight(height)
33 , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID)
34 {
35 SkASSERT(width > 0);
36 SkASSERT(height > 0);
37 }
38
peekPixels(SkImageInfo * info,size_t * rowBytes) const39 const void* SkImage::peekPixels(SkImageInfo* info, size_t* rowBytes) const {
40 SkImageInfo infoStorage;
41 size_t rowBytesStorage;
42 if (nullptr == info) {
43 info = &infoStorage;
44 }
45 if (nullptr == rowBytes) {
46 rowBytes = &rowBytesStorage;
47 }
48 return as_IB(this)->onPeekPixels(info, rowBytes);
49 }
50
readPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint chint) const51 bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
52 int srcX, int srcY, CachingHint chint) const {
53 SkReadPixelsRec rec(dstInfo, dstPixels, dstRowBytes, srcX, srcY);
54 if (!rec.trim(this->width(), this->height())) {
55 return false;
56 }
57 return as_IB(this)->onReadPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, chint);
58 }
59
scalePixels(const SkPixmap & dst,SkFilterQuality quality,CachingHint chint) const60 bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingHint chint) const {
61 if (this->width() == dst.width() && this->height() == dst.height()) {
62 return this->readPixels(dst, 0, 0, chint);
63 }
64
65 // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
66 // can scale more efficiently) we should take advantage of it here.
67 //
68 SkBitmap bm;
69 if (as_IB(this)->getROPixels(&bm, chint)) {
70 bm.lockPixels();
71 SkPixmap pmap;
72 // Note: By calling the pixmap scaler, we never cache the final result, so the chint
73 // is (currently) only being applied to the getROPixels. If we get a request to
74 // also attempt to cache the final (scaled) result, we would add that logic here.
75 //
76 return bm.peekPixels(&pmap) && pmap.scalePixels(dst, quality);
77 }
78 return false;
79 }
80
preroll(GrContext * ctx) const81 void SkImage::preroll(GrContext* ctx) const {
82 // For now, and to maintain parity w/ previous pixelref behavior, we just force the image
83 // to produce a cached raster-bitmap form, so that drawing to a raster canvas should be fast.
84 //
85 SkBitmap bm;
86 if (as_IB(this)->getROPixels(&bm)) {
87 bm.lockPixels();
88 bm.unlockPixels();
89 }
90 }
91
92 ///////////////////////////////////////////////////////////////////////////////////////////////////
93
newShader(SkShader::TileMode tileX,SkShader::TileMode tileY,const SkMatrix * localMatrix) const94 SkShader* SkImage::newShader(SkShader::TileMode tileX,
95 SkShader::TileMode tileY,
96 const SkMatrix* localMatrix) const {
97 return SkImageShader::Create(this, tileX, tileY, localMatrix);
98 }
99
encode(SkImageEncoder::Type type,int quality) const100 SkData* SkImage::encode(SkImageEncoder::Type type, int quality) const {
101 SkBitmap bm;
102 if (as_IB(this)->getROPixels(&bm)) {
103 return SkImageEncoder::EncodeData(bm, type, quality);
104 }
105 return nullptr;
106 }
107
encode(SkPixelSerializer * serializer) const108 SkData* SkImage::encode(SkPixelSerializer* serializer) const {
109 SkAutoTUnref<SkPixelSerializer> defaultSerializer;
110 SkPixelSerializer* effectiveSerializer = serializer;
111 if (!effectiveSerializer) {
112 defaultSerializer.reset(SkImageEncoder::CreatePixelSerializer());
113 SkASSERT(defaultSerializer.get());
114 effectiveSerializer = defaultSerializer.get();
115 }
116 SkAutoTUnref<SkData> encoded(this->refEncoded());
117 if (encoded && effectiveSerializer->useEncodedData(encoded->data(), encoded->size())) {
118 return encoded.detach();
119 }
120
121 SkBitmap bm;
122 SkAutoPixmapUnlock apu;
123 if (as_IB(this)->getROPixels(&bm) && bm.requestLock(&apu)) {
124 return effectiveSerializer->encode(apu.pixmap());
125 }
126
127 return nullptr;
128 }
129
refEncoded() const130 SkData* SkImage::refEncoded() const {
131 GrContext* ctx = nullptr; // should we allow the caller to pass in a ctx?
132 return as_IB(this)->onRefEncoded(ctx);
133 }
134
NewFromEncoded(SkData * encoded,const SkIRect * subset)135 SkImage* SkImage::NewFromEncoded(SkData* encoded, const SkIRect* subset) {
136 if (nullptr == encoded || 0 == encoded->size()) {
137 return nullptr;
138 }
139 SkImageGenerator* generator = SkImageGenerator::NewFromEncoded(encoded);
140 return generator ? SkImage::NewFromGenerator(generator, subset) : nullptr;
141 }
142
toString(SkString * str) const143 const char* SkImage::toString(SkString* str) const {
144 str->appendf("image: (id:%d (%d, %d) %s)", this->uniqueID(), this->width(), this->height(),
145 this->isOpaque() ? "opaque" : "");
146 return str->c_str();
147 }
148
newSubset(const SkIRect & subset) const149 SkImage* SkImage::newSubset(const SkIRect& subset) const {
150 if (subset.isEmpty()) {
151 return nullptr;
152 }
153
154 const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
155 if (!bounds.contains(subset)) {
156 return nullptr;
157 }
158
159 // optimization : return self if the subset == our bounds
160 if (bounds == subset) {
161 return SkRef(const_cast<SkImage*>(this));
162 }
163 return as_IB(this)->onNewSubset(subset);
164 }
165
166 #if SK_SUPPORT_GPU
167
getTexture() const168 GrTexture* SkImage::getTexture() const {
169 return as_IB(this)->peekTexture();
170 }
171
isTextureBacked() const172 bool SkImage::isTextureBacked() const { return SkToBool(as_IB(this)->getTexture()); }
173
getTextureHandle(bool flushPendingGrContextIO) const174 GrBackendObject SkImage::getTextureHandle(bool flushPendingGrContextIO) const {
175 GrTexture* texture = as_IB(this)->getTexture();
176 if (texture) {
177 GrContext* context = texture->getContext();
178 if (context) {
179 if (flushPendingGrContextIO) {
180 context->prepareSurfaceForExternalIO(texture);
181 }
182 }
183 return texture->getTextureHandle();
184 }
185 return 0;
186 }
187
188 #else
189
getTexture() const190 GrTexture* SkImage::getTexture() const { return nullptr; }
191
isTextureBacked() const192 bool SkImage::isTextureBacked() const { return false; }
193
getTextureHandle(bool) const194 GrBackendObject SkImage::getTextureHandle(bool) const { return 0; }
195
196 #endif
197
198 ///////////////////////////////////////////////////////////////////////////////
199
raster_canvas_supports(const SkImageInfo & info)200 static bool raster_canvas_supports(const SkImageInfo& info) {
201 switch (info.colorType()) {
202 case kN32_SkColorType:
203 return kUnpremul_SkAlphaType != info.alphaType();
204 case kRGB_565_SkColorType:
205 return true;
206 case kAlpha_8_SkColorType:
207 return true;
208 default:
209 break;
210 }
211 return false;
212 }
213
SkImage_Base(int width,int height,uint32_t uniqueID)214 SkImage_Base::SkImage_Base(int width, int height, uint32_t uniqueID)
215 : INHERITED(width, height, uniqueID)
216 , fAddedToCache(false)
217 {}
218
~SkImage_Base()219 SkImage_Base::~SkImage_Base() {
220 if (fAddedToCache.load()) {
221 SkNotifyBitmapGenIDIsStale(this->uniqueID());
222 }
223 }
224
onReadPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRowBytes,int srcX,int srcY,CachingHint) const225 bool SkImage_Base::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
226 int srcX, int srcY, CachingHint) const {
227 if (!raster_canvas_supports(dstInfo)) {
228 return false;
229 }
230
231 SkBitmap bm;
232 bm.installPixels(dstInfo, dstPixels, dstRowBytes);
233 SkCanvas canvas(bm);
234
235 SkPaint paint;
236 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
237 canvas.drawImage(this, -SkIntToScalar(srcX), -SkIntToScalar(srcY), &paint);
238
239 return true;
240 }
241
242 ///////////////////////////////////////////////////////////////////////////////////////////////////
243
peekPixels(SkPixmap * pmap) const244 bool SkImage::peekPixels(SkPixmap* pmap) const {
245 SkImageInfo info;
246 size_t rowBytes;
247 const void* pixels = this->peekPixels(&info, &rowBytes);
248 if (pixels) {
249 if (pmap) {
250 pmap->reset(info, pixels, rowBytes);
251 }
252 return true;
253 }
254 return false;
255 }
256
readPixels(const SkPixmap & pmap,int srcX,int srcY,CachingHint chint) const257 bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
258 return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint);
259 }
260
261 #if SK_SUPPORT_GPU
262 #include "GrTextureToYUVPlanes.h"
263 #endif
264
265 #include "SkRGBAToYUV.h"
266
readYUV8Planes(const SkISize sizes[3],void * const planes[3],const size_t rowBytes[3],SkYUVColorSpace colorSpace) const267 bool SkImage::readYUV8Planes(const SkISize sizes[3], void* const planes[3],
268 const size_t rowBytes[3], SkYUVColorSpace colorSpace) const {
269 #if SK_SUPPORT_GPU
270 if (GrTexture* texture = as_IB(this)->peekTexture()) {
271 if (GrTextureToYUVPlanes(texture, sizes, planes, rowBytes, colorSpace)) {
272 return true;
273 }
274 }
275 #endif
276 return SkRGBAToYUV(this, sizes, planes, rowBytes, colorSpace);
277 }
278
279 ///////////////////////////////////////////////////////////////////////////////////////////////////
280
NewFromBitmap(const SkBitmap & bm)281 SkImage* SkImage::NewFromBitmap(const SkBitmap& bm) {
282 SkPixelRef* pr = bm.pixelRef();
283 if (nullptr == pr) {
284 return nullptr;
285 }
286
287 #if SK_SUPPORT_GPU
288 if (GrTexture* tex = pr->getTexture()) {
289 SkAutoTUnref<GrTexture> unrefCopy;
290 if (!bm.isImmutable()) {
291 tex = GrDeepCopyTexture(tex, SkBudgeted::kNo);
292 if (nullptr == tex) {
293 return nullptr;
294 }
295 unrefCopy.reset(tex);
296 }
297 const SkImageInfo info = bm.info();
298 return new SkImage_Gpu(info.width(), info.height(), bm.getGenerationID(), info.alphaType(),
299 tex, SkBudgeted::kNo);
300 }
301 #endif
302
303 // This will check for immutable (share or copy)
304 return SkNewImageFromRasterBitmap(bm);
305 }
306
asLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode mode) const307 bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
308 return as_IB(this)->onAsLegacyBitmap(bitmap, mode);
309 }
310
onAsLegacyBitmap(SkBitmap * bitmap,LegacyBitmapMode mode) const311 bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
312 // As the base-class, all we can do is make a copy (regardless of mode).
313 // Subclasses that want to be more optimal should override.
314 SkImageInfo info = SkImageInfo::MakeN32(this->width(), this->height(),
315 this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
316 if (!bitmap->tryAllocPixels(info)) {
317 return false;
318 }
319 if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
320 bitmap->reset();
321 return false;
322 }
323
324 if (kRO_LegacyBitmapMode == mode) {
325 bitmap->setImmutable();
326 }
327 return true;
328 }
329
NewFromPicture(const SkPicture * picture,const SkISize & dimensions,const SkMatrix * matrix,const SkPaint * paint)330 SkImage* SkImage::NewFromPicture(const SkPicture* picture, const SkISize& dimensions,
331 const SkMatrix* matrix, const SkPaint* paint) {
332 if (!picture) {
333 return nullptr;
334 }
335 return NewFromGenerator(SkImageGenerator::NewFromPicture(dimensions, picture, matrix, paint));
336 }
337
isLazyGenerated() const338 bool SkImage::isLazyGenerated() const {
339 return as_IB(this)->onIsLazyGenerated();
340 }
341
342 //////////////////////////////////////////////////////////////////////////////////////
343
344 #if !SK_SUPPORT_GPU
345
NewFromTexture(GrContext *,const GrBackendTextureDesc &,SkAlphaType,TextureReleaseProc,ReleaseContext)346 SkImage* SkImage::NewFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType,
347 TextureReleaseProc, ReleaseContext) {
348 return nullptr;
349 }
350
NewFromAdoptedTexture(GrContext *,const GrBackendTextureDesc &,SkAlphaType)351 SkImage* SkImage::NewFromAdoptedTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType) {
352 return nullptr;
353 }
354
NewFromTextureCopy(GrContext *,const GrBackendTextureDesc &,SkAlphaType)355 SkImage* SkImage::NewFromTextureCopy(GrContext*, const GrBackendTextureDesc&, SkAlphaType) {
356 return nullptr;
357 }
358
newTextureImage(GrContext *) const359 SkImage* SkImage::newTextureImage(GrContext*) const {
360 return nullptr;
361 }
362
363 #endif
364