SkImage.cpp revision 26ecfe0af8da1d17a079e17af85c5c576bfeca84
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 "SkImageFilter.h"
14#include "SkImageFilterCache.h"
15#include "SkImageGenerator.h"
16#include "SkImagePriv.h"
17#include "SkImageShader.h"
18#include "SkImage_Base.h"
19#include "SkNextID.h"
20#include "SkPicture.h"
21#include "SkPixelRef.h"
22#include "SkPixelSerializer.h"
23#include "SkReadPixelsRec.h"
24#include "SkSpecialImage.h"
25#include "SkStream.h"
26#include "SkString.h"
27#include "SkSurface.h"
28
29#if SK_SUPPORT_GPU
30#include "GrTexture.h"
31#include "GrContext.h"
32#include "SkImage_Gpu.h"
33#endif
34
35SkImage::SkImage(int width, int height, uint32_t uniqueID)
36    : fWidth(width)
37    , fHeight(height)
38    , fUniqueID(kNeedNewImageUniqueID == uniqueID ? SkNextID::ImageID() : uniqueID)
39{
40    SkASSERT(width > 0);
41    SkASSERT(height > 0);
42}
43
44bool SkImage::peekPixels(SkPixmap* pm) const {
45    SkPixmap tmp;
46    if (!pm) {
47        pm = &tmp;
48    }
49    return as_IB(this)->onPeekPixels(pm);
50}
51
52bool SkImage::readPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes,
53                           int srcX, int srcY, CachingHint chint) const {
54    SkReadPixelsRec rec(dstInfo, dstPixels, dstRowBytes, srcX, srcY);
55    if (!rec.trim(this->width(), this->height())) {
56        return false;
57    }
58    return as_IB(this)->onReadPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, chint);
59}
60
61bool SkImage::scalePixels(const SkPixmap& dst, SkFilterQuality quality, CachingHint chint) const {
62    if (this->width() == dst.width() && this->height() == dst.height()) {
63        return this->readPixels(dst, 0, 0, chint);
64    }
65
66    // Idea: If/when SkImageGenerator supports a native-scaling API (where the generator itself
67    //       can scale more efficiently) we should take advantage of it here.
68    //
69    SkBitmap bm;
70    if (as_IB(this)->getROPixels(&bm, dst.info().colorSpace(), chint)) {
71        bm.lockPixels();
72        SkPixmap pmap;
73        // Note: By calling the pixmap scaler, we never cache the final result, so the chint
74        //       is (currently) only being applied to the getROPixels. If we get a request to
75        //       also attempt to cache the final (scaled) result, we would add that logic here.
76        //
77        return bm.peekPixels(&pmap) && pmap.scalePixels(dst, quality);
78    }
79    return false;
80}
81
82///////////////////////////////////////////////////////////////////////////////////////////////////
83
84SkAlphaType SkImage::alphaType() const {
85    return as_IB(this)->onAlphaType();
86}
87
88sk_sp<SkShader> SkImage::makeShader(SkShader::TileMode tileX, SkShader::TileMode tileY,
89                                    const SkMatrix* localMatrix) const {
90    return SkImageShader::Make(sk_ref_sp(const_cast<SkImage*>(this)), tileX, tileY, localMatrix);
91}
92
93SkData* SkImage::encode(SkEncodedImageFormat type, int quality) const {
94    SkBitmap bm;
95    SkColorSpace* legacyColorSpace = nullptr;
96    if (as_IB(this)->getROPixels(&bm, legacyColorSpace)) {
97        SkDynamicMemoryWStream buf;
98        return SkEncodeImage(&buf, bm, type, quality) ? buf.detachAsData().release() : nullptr;
99    }
100    return nullptr;
101}
102
103SkData* SkImage::encode(SkPixelSerializer* serializer) const {
104    sk_sp<SkData> encoded(this->refEncoded());
105    if (encoded &&
106        (!serializer || serializer->useEncodedData(encoded->data(), encoded->size()))) {
107        return encoded.release();
108    }
109
110    SkBitmap bm;
111    SkAutoPixmapUnlock apu;
112    SkColorSpace* legacyColorSpace = nullptr;
113    if (as_IB(this)->getROPixels(&bm, legacyColorSpace) &&
114        bm.requestLock(&apu)) {
115        if (serializer) {
116            return serializer->encode(apu.pixmap());
117        } else {
118            SkDynamicMemoryWStream buf;
119            return SkEncodeImage(&buf, apu.pixmap(), SkEncodedImageFormat::kPNG, 100)
120                   ? buf.detachAsData().release() : nullptr;
121        }
122    }
123
124    return nullptr;
125}
126
127SkData* SkImage::refEncoded() const {
128    GrContext* ctx = nullptr;   // should we allow the caller to pass in a ctx?
129    return as_IB(this)->onRefEncoded(ctx);
130}
131
132sk_sp<SkImage> SkImage::MakeFromEncoded(sk_sp<SkData> encoded, const SkIRect* subset) {
133    if (nullptr == encoded || 0 == encoded->size()) {
134        return nullptr;
135    }
136    SkImageGenerator* generator = SkImageGenerator::NewFromEncoded(encoded.get());
137    return SkImage::MakeFromGenerator(generator, subset);
138}
139
140const char* SkImage::toString(SkString* str) const {
141    str->appendf("image: (id:%d (%d, %d) %s)", this->uniqueID(), this->width(), this->height(),
142                 this->isOpaque() ? "opaque" : "");
143    return str->c_str();
144}
145
146sk_sp<SkImage> SkImage::makeSubset(const SkIRect& subset) const {
147    if (subset.isEmpty()) {
148        return nullptr;
149    }
150
151    const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
152    if (!bounds.contains(subset)) {
153        return nullptr;
154    }
155
156    // optimization : return self if the subset == our bounds
157    if (bounds == subset) {
158        return sk_ref_sp(const_cast<SkImage*>(this));
159    }
160    return as_IB(this)->onMakeSubset(subset);
161}
162
163#if SK_SUPPORT_GPU
164
165GrTexture* SkImage::getTexture() const {
166    return as_IB(this)->peekTexture();
167}
168
169bool SkImage::isTextureBacked() const { return SkToBool(as_IB(this)->peekTexture()); }
170
171GrBackendObject SkImage::getTextureHandle(bool flushPendingGrContextIO) const {
172    GrTexture* texture = as_IB(this)->peekTexture();
173    if (texture) {
174        GrContext* context = texture->getContext();
175        if (context) {
176            if (flushPendingGrContextIO) {
177                context->prepareSurfaceForExternalIO(texture);
178            }
179        }
180        return texture->getTextureHandle();
181    }
182    return 0;
183}
184
185#else
186
187GrTexture* SkImage::getTexture() const { return nullptr; }
188
189bool SkImage::isTextureBacked() const { return false; }
190
191GrBackendObject SkImage::getTextureHandle(bool) const { return 0; }
192
193#endif
194
195///////////////////////////////////////////////////////////////////////////////
196
197SkImage_Base::SkImage_Base(int width, int height, uint32_t uniqueID)
198    : INHERITED(width, height, uniqueID)
199    , fAddedToCache(false)
200{}
201
202SkImage_Base::~SkImage_Base() {
203    if (fAddedToCache.load()) {
204        SkNotifyBitmapGenIDIsStale(this->uniqueID());
205    }
206}
207
208///////////////////////////////////////////////////////////////////////////////////////////////////
209
210bool SkImage::readPixels(const SkPixmap& pmap, int srcX, int srcY, CachingHint chint) const {
211    return this->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), srcX, srcY, chint);
212}
213
214#if SK_SUPPORT_GPU
215#include "GrTextureToYUVPlanes.h"
216#endif
217
218#include "SkRGBAToYUV.h"
219
220bool SkImage::readYUV8Planes(const SkISize sizes[3], void* const planes[3],
221                             const size_t rowBytes[3], SkYUVColorSpace colorSpace) const {
222#if SK_SUPPORT_GPU
223    if (GrTexture* texture = as_IB(this)->peekTexture()) {
224        if (GrTextureToYUVPlanes(texture, sizes, planes, rowBytes, colorSpace)) {
225            return true;
226        }
227    }
228#endif
229    return SkRGBAToYUV(this, sizes, planes, rowBytes, colorSpace);
230}
231
232///////////////////////////////////////////////////////////////////////////////////////////////////
233
234sk_sp<SkImage> SkImage::MakeFromBitmap(const SkBitmap& bm) {
235    SkPixelRef* pr = bm.pixelRef();
236    if (nullptr == pr) {
237        return nullptr;
238    }
239
240    return SkMakeImageFromRasterBitmap(bm, kIfMutable_SkCopyPixelsMode);
241}
242
243bool SkImage::asLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
244    return as_IB(this)->onAsLegacyBitmap(bitmap, mode);
245}
246
247bool SkImage_Base::onAsLegacyBitmap(SkBitmap* bitmap, LegacyBitmapMode mode) const {
248    // As the base-class, all we can do is make a copy (regardless of mode).
249    // Subclasses that want to be more optimal should override.
250    SkImageInfo info = this->onImageInfo().makeColorType(kN32_SkColorType).makeColorSpace(nullptr);
251    if (!bitmap->tryAllocPixels(info)) {
252        return false;
253    }
254    if (!this->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0)) {
255        bitmap->reset();
256        return false;
257    }
258
259    if (kRO_LegacyBitmapMode == mode) {
260        bitmap->setImmutable();
261    }
262    return true;
263}
264
265sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
266                                        const SkMatrix* matrix, const SkPaint* paint) {
267    return SkImage::MakeFromPicture(std::move(picture), dimensions, matrix, paint, BitDepth::kU8,
268                                    nullptr);
269}
270
271sk_sp<SkImage> SkImage::MakeFromPicture(sk_sp<SkPicture> picture, const SkISize& dimensions,
272                                        const SkMatrix* matrix, const SkPaint* paint,
273                                        BitDepth bitDepth, sk_sp<SkColorSpace> colorSpace) {
274    return MakeFromGenerator(SkImageGenerator::NewFromPicture(dimensions, picture.get(), matrix,
275                                                              paint, bitDepth,
276                                                              std::move(colorSpace)));
277}
278sk_sp<SkImage> SkImage::makeWithFilter(const SkImageFilter* filter, const SkIRect& subset,
279                                       const SkIRect& clipBounds, SkIRect* outSubset,
280                                       SkIPoint* offset) const {
281    if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
282        return nullptr;
283    }
284    SkColorSpace* colorSpace = as_IB(this)->onImageInfo().colorSpace();
285    sk_sp<SkSpecialImage> srcSpecialImage = SkSpecialImage::MakeFromImage(
286        subset, sk_ref_sp(const_cast<SkImage*>(this)), colorSpace);
287    if (!srcSpecialImage) {
288        return nullptr;
289    }
290
291    sk_sp<SkImageFilterCache> cache(
292        SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
293    SkImageFilter::OutputProperties outputProperties(colorSpace);
294    SkImageFilter::Context context(SkMatrix::I(), clipBounds, cache.get(), outputProperties);
295
296    sk_sp<SkSpecialImage> result =
297        filter->filterImage(srcSpecialImage.get(), context, offset);
298
299    if (!result) {
300        return nullptr;
301    }
302
303    SkIRect fullSize = SkIRect::MakeWH(result->width(), result->height());
304#if SK_SUPPORT_GPU
305    if (result->isTextureBacked()) {
306        GrContext* context = result->getContext();
307        sk_sp<GrTexture> texture = result->asTextureRef(context);
308        if (!texture) {
309            return nullptr;
310        }
311        fullSize = SkIRect::MakeWH(texture->width(), texture->height());
312    }
313#endif
314    *outSubset = SkIRect::MakeWH(result->width(), result->height());
315    if (!outSubset->intersect(clipBounds.makeOffset(-offset->x(), -offset->y()))) {
316        return nullptr;
317    }
318    offset->fX += outSubset->x();
319    offset->fY += outSubset->y();
320    // This isn't really a "tight" subset, but includes any texture padding.
321    return result->makeTightSubset(fullSize);
322}
323
324bool SkImage::isLazyGenerated() const {
325    return as_IB(this)->onIsLazyGenerated();
326}
327
328bool SkImage::isAlphaOnly() const {
329    return as_IB(this)->onImageInfo().colorType() == kAlpha_8_SkColorType;
330}
331
332//////////////////////////////////////////////////////////////////////////////////////
333
334#if !SK_SUPPORT_GPU
335
336sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel* texels,
337                                     int mipLevelCount, SkBudgeted, SkDestinationSurfaceColorMode) {
338    return nullptr;
339}
340
341sk_sp<SkImage> SkImage::MakeFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType,
342                                        sk_sp<SkColorSpace>, TextureReleaseProc, ReleaseContext) {
343    return nullptr;
344}
345
346size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&,
347                                            const DeferredTextureImageUsageParams[],
348                                            int paramCnt, void* buffer,
349                                            SkColorSpace* dstColorSpace) const {
350    return 0;
351}
352
353sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void*,
354                                                         SkBudgeted) {
355    return nullptr;
356}
357
358sk_sp<SkImage> SkImage::MakeFromAdoptedTexture(GrContext*, const GrBackendTextureDesc&,
359                                               SkAlphaType, sk_sp<SkColorSpace>) {
360    return nullptr;
361}
362
363sk_sp<SkImage> SkImage::MakeFromYUVTexturesCopy(GrContext* ctx, SkYUVColorSpace space,
364                                                const GrBackendObject yuvTextureHandles[3],
365                                                const SkISize yuvSizes[3],
366                                                GrSurfaceOrigin origin,
367                                                sk_sp<SkColorSpace> imageColorSpace) {
368    return nullptr;
369}
370
371sk_sp<SkImage> SkImage::makeNonTextureImage() const {
372    return sk_ref_sp(const_cast<SkImage*>(this));
373}
374
375#endif
376
377///////////////////////////////////////////////////////////////////////////////////////////////////
378
379sk_sp<SkImage> MakeTextureFromMipMap(GrContext*, const SkImageInfo&, const GrMipLevel* texels,
380                                     int mipLevelCount, SkBudgeted) {
381    return nullptr;
382}
383
384///////////////////////////////////////////////////////////////////////////////////////////////////
385#include "SkImageDeserializer.h"
386
387sk_sp<SkImage> SkImageDeserializer::makeFromData(SkData* data, const SkIRect* subset) {
388    return SkImage::MakeFromEncoded(sk_ref_sp(data), subset);
389}
390sk_sp<SkImage> SkImageDeserializer::makeFromMemory(const void* data, size_t length,
391                                                   const SkIRect* subset) {
392    return SkImage::MakeFromEncoded(SkData::MakeWithCopy(data, length), subset);
393}
394
395///////////////////////////////////////////////////////////////////////////////////////////////////
396
397bool SkImage_pinAsTexture(const SkImage* image, GrContext* ctx) {
398    SkASSERT(image);
399    SkASSERT(ctx);
400    return as_IB(image)->onPinAsTexture(ctx);
401}
402
403void SkImage_unpinAsTexture(const SkImage* image, GrContext* ctx) {
404    SkASSERT(image);
405    SkASSERT(ctx);
406    as_IB(image)->onUnpinAsTexture(ctx);
407}
408
409///////////////////////////////////////////////////////////////////////////////////////////////////
410
411sk_sp<SkImage> SkImageMakeRasterCopyAndAssignColorSpace(const SkImage* src,
412                                                        SkColorSpace* colorSpace) {
413    // Read the pixels out of the source image, with no conversion
414    SkImageInfo info = as_IB(src)->onImageInfo();
415    if (kUnknown_SkColorType == info.colorType()) {
416        SkDEBUGFAIL("Unexpected color type");
417        return nullptr;
418    }
419
420    size_t rowBytes = info.minRowBytes();
421    size_t size = info.getSafeSize(rowBytes);
422    auto data = SkData::MakeUninitialized(size);
423    if (!data) {
424        return nullptr;
425    }
426
427    SkPixmap pm(info, data->writable_data(), rowBytes);
428    if (!src->readPixels(pm, 0, 0, SkImage::kDisallow_CachingHint)) {
429        return nullptr;
430    }
431
432    // Wrap them in a new image with a different color space
433    return SkImage::MakeRasterData(info.makeColorSpace(sk_ref_sp(colorSpace)), data, rowBytes);
434}
435