2 * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
3 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #include "platform/graphics/Image.h"
30 #include "platform/Length.h"
31 #include "platform/MIMETypeRegistry.h"
32 #include "platform/PlatformInstrumentation.h"
33 #include "platform/RuntimeEnabledFeatures.h"
34 #include "platform/SharedBuffer.h"
35 #include "platform/TraceEvent.h"
36 #include "platform/geometry/FloatPoint.h"
37 #include "platform/geometry/FloatRect.h"
38 #include "platform/geometry/FloatSize.h"
39 #include "platform/graphics/BitmapImage.h"
40 #include "platform/graphics/DeferredImageDecoder.h"
41 #include "platform/graphics/GraphicsContext.h"
42 #include "public/platform/Platform.h"
43 #include "public/platform/WebData.h"
44 #include "third_party/skia/include/core/SkCanvas.h"
45 #include "third_party/skia/include/core/SkImage.h"
46 #include "third_party/skia/include/core/SkPictureRecorder.h"
47 #include "wtf/MainThread.h"
48 #include "wtf/StdLibExtras.h"
54 Image::Image(ImageObserver
* observer
)
55 : m_imageObserver(observer
)
63 Image
* Image::nullImage()
65 ASSERT(isMainThread());
66 DEFINE_STATIC_REF(Image
, nullImage
, (BitmapImage::create()));
70 PassRefPtr
<Image
> Image::loadPlatformResource(const char *name
)
72 const WebData
& resource
= Platform::current()->loadResource(name
);
73 if (resource
.isEmpty())
74 return Image::nullImage();
76 RefPtr
<Image
> image
= BitmapImage::create();
77 image
->setData(resource
, true);
78 return image
.release();
81 bool Image::supportsType(const String
& type
)
83 return MIMETypeRegistry::isSupportedImageResourceMIMEType(type
);
86 bool Image::setData(PassRefPtr
<SharedBuffer
> data
, bool allDataReceived
)
88 m_encodedImageData
= data
;
89 if (!m_encodedImageData
.get())
92 int length
= m_encodedImageData
->size();
96 return dataChanged(allDataReceived
);
99 void Image::drawTiled(GraphicsContext
* ctxt
, const FloatRect
& destRect
, const FloatPoint
& srcPoint
, const FloatSize
& scaledTileSize
, SkXfermode::Mode op
, const IntSize
& repeatSpacing
)
101 FloatSize intrinsicTileSize
= size();
102 if (hasRelativeWidth())
103 intrinsicTileSize
.setWidth(scaledTileSize
.width());
104 if (hasRelativeHeight())
105 intrinsicTileSize
.setHeight(scaledTileSize
.height());
107 FloatSize
scale(scaledTileSize
.width() / intrinsicTileSize
.width(),
108 scaledTileSize
.height() / intrinsicTileSize
.height());
110 FloatSize
actualTileSize(scaledTileSize
.width() + repeatSpacing
.width(), scaledTileSize
.height() + repeatSpacing
.height());
111 FloatRect oneTileRect
;
112 oneTileRect
.setX(destRect
.x() + fmodf(fmodf(-srcPoint
.x(), actualTileSize
.width()) - actualTileSize
.width(), actualTileSize
.width()));
113 oneTileRect
.setY(destRect
.y() + fmodf(fmodf(-srcPoint
.y(), actualTileSize
.height()) - actualTileSize
.height(), actualTileSize
.height()));
114 oneTileRect
.setSize(scaledTileSize
);
116 // Check and see if a single draw of the image can cover the entire area we are supposed to tile.
117 if (oneTileRect
.contains(destRect
)) {
118 FloatRect visibleSrcRect
;
119 visibleSrcRect
.setX((destRect
.x() - oneTileRect
.x()) / scale
.width());
120 visibleSrcRect
.setY((destRect
.y() - oneTileRect
.y()) / scale
.height());
121 visibleSrcRect
.setWidth(destRect
.width() / scale
.width());
122 visibleSrcRect
.setHeight(destRect
.height() / scale
.height());
123 ctxt
->drawImage(this, destRect
, visibleSrcRect
, op
, DoNotRespectImageOrientation
);
127 FloatRect
tileRect(FloatPoint(), intrinsicTileSize
);
128 drawPattern(ctxt
, tileRect
, scale
, oneTileRect
.location(), op
, destRect
, repeatSpacing
);
133 // FIXME: Merge with the other drawTiled eventually, since we need a combination of both for some things.
134 void Image::drawTiled(GraphicsContext
* ctxt
, const FloatRect
& dstRect
, const FloatRect
& srcRect
,
135 const FloatSize
& providedTileScaleFactor
, TileRule hRule
, TileRule vRule
, SkXfermode::Mode op
)
137 // FIXME: We do not support 'space' yet. For now just map it to 'repeat'.
138 if (hRule
== SpaceTile
)
140 if (vRule
== SpaceTile
)
143 // FIXME: if this code is used for background-repeat: round (in addition to
144 // border-image-repeat), then add logic to deal with the background-size: auto
145 // special case. The aspect ratio should be maintained in this case.
146 FloatSize tileScaleFactor
= providedTileScaleFactor
;
147 bool useLowInterpolationQuality
= false;
148 if (hRule
== RoundTile
) {
149 float hRepetitions
= std::max(1.0f
, roundf(dstRect
.width() / (tileScaleFactor
.width() * srcRect
.width())));
150 tileScaleFactor
.setWidth(dstRect
.width() / (srcRect
.width() * hRepetitions
));
152 if (vRule
== RoundTile
) {
153 float vRepetitions
= std::max(1.0f
, roundf(dstRect
.height() / (tileScaleFactor
.height() * srcRect
.height())));
154 tileScaleFactor
.setHeight(dstRect
.height() / (srcRect
.height() * vRepetitions
));
156 if (hRule
== RoundTile
|| vRule
== RoundTile
) {
157 // High interpolation quality rounds the scaled tile to an integer size (see Image::drawPattern).
158 // To avoid causing a visual problem, linear interpolation must be used instead.
159 // FIXME: Allow using high-quality interpolation in this case, too.
160 useLowInterpolationQuality
= true;
163 // We want to construct the phase such that the pattern is centered (when stretch is not
164 // set for a particular rule).
165 float hPhase
= tileScaleFactor
.width() * srcRect
.x();
166 float vPhase
= tileScaleFactor
.height() * srcRect
.y();
167 float scaledTileWidth
= tileScaleFactor
.width() * srcRect
.width();
168 float scaledTileHeight
= tileScaleFactor
.height() * srcRect
.height();
169 if (hRule
== Image::RepeatTile
)
170 hPhase
-= (dstRect
.width() - scaledTileWidth
) / 2;
171 if (vRule
== Image::RepeatTile
)
172 vPhase
-= (dstRect
.height() - scaledTileHeight
) / 2;
173 FloatPoint
patternPhase(dstRect
.x() - hPhase
, dstRect
.y() - vPhase
);
175 if (useLowInterpolationQuality
) {
176 InterpolationQuality previousInterpolationQuality
= ctxt
->imageInterpolationQuality();
177 ctxt
->setImageInterpolationQuality(InterpolationLow
);
178 drawPattern(ctxt
, srcRect
, tileScaleFactor
, patternPhase
, op
, dstRect
);
179 ctxt
->setImageInterpolationQuality(previousInterpolationQuality
);
181 drawPattern(ctxt
, srcRect
, tileScaleFactor
, patternPhase
, op
, dstRect
);
189 PassRefPtr
<SkShader
> createPatternShader(const SkImage
* image
, const SkMatrix
& shaderMatrix
,
190 const SkPaint
& paint
, const FloatSize
& spacing
)
192 if (spacing
.isZero())
193 return adoptRef(image
->newShader(SkShader::kRepeat_TileMode
, SkShader::kRepeat_TileMode
, &shaderMatrix
));
195 // Arbitrary tiling is currently only supported for SkPictureShader - so we use it instead
196 // of a plain bitmap shader to implement spacing.
197 const SkRect tileRect
= SkRect::MakeWH(
198 image
->width() + spacing
.width(),
199 image
->height() + spacing
.height());
201 SkPictureRecorder recorder
;
202 SkCanvas
* canvas
= recorder
.beginRecording(tileRect
);
203 canvas
->drawImage(image
, 0, 0, &paint
);
204 RefPtr
<const SkPicture
> picture
= adoptRef(recorder
.endRecordingAsPicture());
206 return adoptRef(SkShader::CreatePictureShader(
207 picture
.get(), SkShader::kRepeat_TileMode
, SkShader::kRepeat_TileMode
, &shaderMatrix
, nullptr));
210 } // anonymous namespace
212 void Image::drawPattern(GraphicsContext
* context
, const FloatRect
& floatSrcRect
, const FloatSize
& scale
,
213 const FloatPoint
& phase
, SkXfermode::Mode compositeOp
, const FloatRect
& destRect
, const IntSize
& repeatSpacing
)
215 TRACE_EVENT0("skia", "Image::drawPattern");
217 RefPtr
<SkImage
> image
= imageForCurrentFrame();
221 FloatRect normSrcRect
= floatSrcRect
;
223 normSrcRect
.intersect(FloatRect(0, 0, image
->width(), image
->height()));
224 if (destRect
.isEmpty() || normSrcRect
.isEmpty())
225 return; // nothing to draw
227 SkMatrix localMatrix
;
228 // We also need to translate it such that the origin of the pattern is the
229 // origin of the destination rect, which is what WebKit expects. Skia uses
230 // the coordinate system origin as the base for the pattern. If WebKit wants
231 // a shifted image, it will shift it from there using the localMatrix.
232 const float adjustedX
= phase
.x() + normSrcRect
.x() * scale
.width();
233 const float adjustedY
= phase
.y() + normSrcRect
.y() * scale
.height();
234 localMatrix
.setTranslate(SkFloatToScalar(adjustedX
), SkFloatToScalar(adjustedY
));
236 // Because no resizing occurred, the shader transform should be
237 // set to the pattern's transform, which just includes scale.
238 localMatrix
.preScale(scale
.width(), scale
.height());
240 // Fetch this now as subsetting may swap the image.
241 auto imageID
= image
->uniqueID();
243 SkIRect srcRect
= enclosingIntRect(normSrcRect
);
244 image
= adoptRef(image
->newImage(srcRect
.width(), srcRect
.height(), &srcRect
));
249 SkPaint paint
= context
->fillPaint();
250 paint
.setColor(SK_ColorBLACK
);
251 paint
.setXfermodeMode(compositeOp
);
252 paint
.setFilterQuality(context
->computeFilterQuality(this, destRect
, normSrcRect
));
253 paint
.setAntiAlias(context
->shouldAntialias());
254 RefPtr
<SkShader
> shader
= createPatternShader(image
.get(), localMatrix
, paint
,
255 FloatSize(repeatSpacing
.width() / scale
.width(), repeatSpacing
.height() / scale
.height()));
256 paint
.setShader(shader
.get());
257 context
->drawRect(destRect
, paint
);
260 if (currentFrameIsLazyDecoded())
261 PlatformInstrumentation::didDrawLazyPixelRef(imageID
);
264 void Image::computeIntrinsicDimensions(Length
& intrinsicWidth
, Length
& intrinsicHeight
, FloatSize
& intrinsicRatio
)
266 intrinsicRatio
= size();
267 intrinsicWidth
= Length(intrinsicRatio
.width(), Fixed
);
268 intrinsicHeight
= Length(intrinsicRatio
.height(), Fixed
);
271 PassRefPtr
<Image
> Image::imageForDefaultFrame()
273 RefPtr
<Image
> image(this);
275 return image
.release();
278 bool Image::deprecatedBitmapForCurrentFrame(SkBitmap
* bitmap
)
280 RefPtr
<SkImage
> image
= imageForCurrentFrame();
282 return image
&& image
->asLegacyBitmap(bitmap
, SkImage::kRO_LegacyBitmapMode
);