Merge "Merge branch 'tizen' into devel/new_mesh" into devel/new_mesh
[platform/core/uifw/dali-toolkit.git] / docs / content / shared-javascript-and-cpp-documentation / resource-image-scaling.md
1 <!--
2 /**-->
3
4 [TOC]
5
6 # Resource Image Scaling {#resourceimagescaling}
7
8 ## Introduction
9
10 While common uses of images in DALi applications involve fixed sized images
11 under the developer's control, e.g. for button backgrounds, in other cases such as galleries and wallpapers an application must display a variety of images and adapt to different screen sizes and densities.
12 For these situations, DALi provides a facility to scale a `ResourceImage` while it is being loaded.
13
14 Look at the following example.
15 Let's say we are writing a home-screen application for a smartphone.
16 Here we have a large, square image that we want to set as the wallpaper on a tall and narrow phone screen.
17 We want to fill the screen without distorting the image or having black borders, and wasting as few pixels from the source image as possible.
18
19 ![ ](../assets/img/image-scaling/example-scale-to-fill-problem.jpg)
20 ![ ](example-scale-to-fill-problem.jpg)
21
22 DALi provides the concept of a `FittingMode` to specify how a source image is mapped into a target rectangle, and the one we need here is `FittingMode::SCALE_TO_FILL` as it will cover all of the pixels of the target.
23 A second concept of a `SamplingMode` controls how source image pixels are combined during the scaling and allows the developer to trade speed for quality.
24 Since our image is to be loaded once and reused, we should use `SamplingMode::BOX_THEN_LINEAR` which is the highest quality option.
25 We can pass the stage dimensions to the `ResourceImage` creator function as the desired rectangle and ask it to map the image to the screen as follows:
26
27 ~~~{.cpp}
28 // C++
29 ResourceImage image = ResourceImage::New(
30   "gallery-large-12.jpg",
31   Dali::ImageDimensions( stage.GetSize().x, stage.GetSize().y ),
32   Dali::FittingMode::SCALE_TO_FILL,
33   Dali::SamplingMode::BOX_THEN_LINEAR );
34 ~~~
35 ~~~{.js}
36 // JavaScript
37 // First get stage dimensions into stageX and stageY ...
38 var image = new dali.ResourceImage( {
39   url: "gallery-large-12.jpg",
40   width: stageX,
41   height: stageY,
42   fitting-mode: "SCALE_TO_FILL",
43   sampling-mode: "BOX_THEN_LINEAR"
44 });
45 ~~~
46
47 In this case, `SCALE_TO_FILL` will perform this sequence of operations:
48
49 | SCALE_TO_FILL Example |
50 | ------------ |
51 | ![ ](../assets/img/image-scaling/example-scale-to-fill-sequence.jpg) ![ ](example-scale-to-fill-sequence.jpg) |
52 | <sub> **1.** *The source image.* **2.** *The source scaled-down to match the screen size.* **3.** *The borders of the image are trimmed to match the shape of the screen.* **4.** *The image fits exactly on the phone screen with no scaling required while rendering.* </sub> |
53
54
55 ## API Details {#resourceimagescalingapidetails}
56
57 The new function of `ResourceImage` has the following scaling-related parameters:
58 * **path**: Identifier for the image (allows raw image width and height to be retrieved).
59 * **requested dimensions**: These are either `(0,0)`, a width, a height, or a (width, height) pair and either directly, or after reading the image raw dimensions and doing some math, define a target rectangle to fit the image to.
60 * **fitting mode**: one of four strategies for mapping images onto the target rectangle.
61 * **sampling mode** Different quality options for the scaling.
62
63 ### Target dimensions for fitting {#resourceimagescalingtargetdimensions}
64
65 An application has several options for specifying the target rectangle for the image to be fitted to.
66 The application may request dimensions through `ResourceImage::New()`:
67
68   1. `(0, 0)`, which is equivalent to not specifying any dimensions
69   2. `(x != 0, 0)` or `(0, y != 0)`, i.e. just one dimension specified
70   3. `(x != 0, y != 0)`, i.e. both dimensions specified
71
72 In **case 1.** no scaling will be attempted.
73 The image will be loaded at its raw dimensions.
74 In **case 2.** the unspecified dimension will be derived from the specified one and the aspect ratio of the raw dimensions.
75 This specified dimension and calculated dimension pair pass on as the target dimension for fitting.
76 See more on this case [below](#resourceimagescalingzerodimensions).
77 In **case 3.** the requested dimensions pass straight through to become the target for fitting.
78
79 The result of this process is an `(x, y)` tuple defining a box to fit the image to in the next step.
80
81 #### Examples {#resourceimagescalingtargetdimensionsexamples}
82 If we have a `(320, 240)` image called "flower.jpg", we use these three options in code as below.
83
84 **Case 1**:
85 ~~~{.cpp}
86 // C++
87 ResourceImage image1 = ResourceImage::New( "flower.png" );
88 ResourceImage image2 = ResourceImage::New( "flower.png", ImageDimensions( 0, 0 ) );
89 ~~~
90 ~~~{.js}
91 // JavaScript
92 var image1 = new dali.ResourceImage( { url:"flower.png" } );
93 var image2 = new dali.ResourceImage( { url:"flower.png", width:0, height:0 } );
94 ~~~
95 In these two equivalent loads, the target dimensions will be `(320, 240)` so the image will be loaded at its raw dimensions without modification.
96
97 **Case 2**:
98 ~~~{.cpp}
99 // C++
100 ResourceImage image1 = ResourceImage::New( "flower.png", ImageDimensions( 160, 0 ) );
101 ResourceImage image2 = ResourceImage::New( "flower.png", ImageDimensions( 0, 120 ) );
102 ~~~
103 ~~~{.js}
104 // JavaScript
105 var image1 = new dali.ResourceImage( { url:"flower.png", width:160, height:0 } );
106 var image2 = new dali.ResourceImage( { url:"flower.png", width:0, height:120 } );
107 ~~~
108 In these loads, the target dimensions will be `(160, 120)` as the zero dimension is derived from the aspect ratio of the raw image.
109
110 **Case 3**:
111 ~~~{.cpp}
112 // C++
113 ResourceImage image = ResourceImage::New( "flower.png", ImageDimensions( 111, 233 ) );
114 ~~~
115 ~~~{.js}
116 // JavaScript
117 var image = new dali.ResourceImage( { url:"flower.png", width:111, height:233 } );
118 ~~~
119 In this load, the target dimensions will be `(111, 233)`.
120
121 ### Image Pixel Dimensions {#resourceimagescalingsamplingmodesdimensionflow}
122
123 DALi derives the pixel width and height of loaded resource images from a sequence of calculations with four inputs:
124
125   1. The dimensions requested via `ResourceImage` new function, either of which may be passed as zeros
126   2. The dimensions of the raw image before loading
127   3. The fitting mode requested
128   4. The sampling mode requested
129
130 The dimensions requested do not correspond 1:1 with the exact pixel width and height of the image once loaded: they are just one of the four inputs to the process which determines those dimensions.
131
132 | Flow of image dimensions from API to loaded image |
133 | ------------ |
134 | ![ ](../assets/img/image-scaling/concept-rectangles.jpg) ![ ](concept-rectangles.jpg) |
135 | <sub> *Image dimensions requested through the API are either absent (**a.**), a height (**b.**), a width (**c.**) or a full (width, height) pair (**d.**). In case **d.** this is the target for fitting (**i.**). In cases **a.**, **b.**, and **c.**, the raw image dimensions (**e.**) are also needed to define the target (examples: **f.**, **g.**, **h.**). This target, the **FittingMode**, and the raw size (**e.**), are used to derive the fitted size (**j.**). This is the size that a perfect scaling would achieve. This fitted size, the **Sampling Mode**, and the raw size (**e.**) are used to derive the final pixel width and height (**k.** **l.** **m.**, depending on **Sampling Mode**).* </sub> |
136
137 The diagram above shows the key `(x,y)` image dimensions used by DALi in its load-time scaling pipeline visualized as rectangles.
138 They are:
139
140   1. **Requested**: The dimensions passed by the app.
141   2. **Raw**: The dimensions stored in the resource.
142   3. **Target**: The box to fit the image into derived from 1. and 2.
143   4. **Fitted**: The ideal scaled-down size of the image.
144   5. **Scaled Pixels** The final pixel width and height resulting from the (possibly approximate) scaling.
145
146 This should help to understand how the parameters given to DALi influence the final image loaded. The (x, y) passed define a *target* to the fit the image to rather than a new size for the image directly.
147 The fitting mode defines a strategy for fitting the raw image to the target.
148 The sampling mode has two options which cause the fitted dimensions to be exceeded, while the others cause it to be matched exactly.
149
150 The pipeline from the values passed from the application to the *natural size* of the image is different. If no dimension is passed, the raw image size is read from the image resource. If only one dimension is passed, the explicitly set dimension will be used for the **natural size** and the unspecified dimension will match the actual loaded pixel dimension. When both are specified that becomes the 'natural size' even if it differs from the actual pixel dimensions loaded. This [requires some care in rendering to avoid distortion](#resourceimagescalingsamplingmodesrendernaturalsize).
151
152 ### Fitting an image's dimensions to the target box {#resourceimagescalingfittingmodes}
153
154 DALi provides a number of strategies for mapping the pixels of an image onto the target box derived above.
155 It provides a `FittingMode` enumeration to the developer to select a mapping or fitting approach.
156 These are `SCALE_TO_FILL`, `SHRINK_TO_FIT`, `FIT_WIDTH`, and `FIT_HEIGHT` and their effect is best appreciated visually:
157
158 | FittingMode Options |
159 | ------------------- |
160 | ![ ](../assets/img/image-scaling/fitting-mode-options.jpg) ![ ](fitting-mode-options.jpg) |
161 | <sub> **Fitting modes**: *The top row shows the effect of each mode when a tall target rectangle is applied to a square image. The middle row applies a wide target to a square raw image. The bottom row uses a target with the same aspect ratio as the raw image. These examples show that `SCALE_TO_FILL` is the only option for which the dimensions of the fitted image result exactly match the target. The others are larger or smaller, with a different aspect ratio. `SHRINK_TO_FIT` is always equal to one of `FIT_WIDTH` or `FIT_HEIGHT`: in each case it is the minimum of them. As a special case, where the aspect ratio of raw image and target match, all fitting modes generate an exact match final image and are equivalent to each other.* </sub> |
162
163 The operation of each of these modes is as follows:
164
165 | `FittingMode` | **Operation** |
166 | ------------- | --------- |
167 | `SCALE_TO_FILL` | Centers the image on the target box and uniformly scales it so that it matches the target in one dimension and extends outside the target in the other. Chooses the dimension to match that results in the fewest pixels outside the target. Trims away the parts of the image outside the target box so as to match it exactly. |
168 | `SHRINK_TO_FIT` | Centers the image on the target box and uniformly scales it so that it matches the target in one dimension and fits inside it in the other. |
169 | `FIT_WIDTH` | Centers the image on the target box and uniformly scales it so that it matches the target width without regard for the target height. |
170 | `FIT_HEIGHT` | Centers the image on the target box and uniformly scales it so that it matches the target in height and ignores the target width. |
171
172 These modes differ only when the target box has a different aspect ratio to the raw image. Using this, if the application knows a priori what the image dimensions are, it can scale down the image by requesting dimensions that have the same aspect ratio as the raw dimensions:
173 ~~~{.cpp}
174 // C++
175 // Image on 'disk' is 320x240.
176 ResourceImage image = ResourceImage::New( "flower.png", ImageDimensions( 32, 24 ) );
177 // Image will be loaded at (32, 24), regardless of fitting mode.
178 ~~~
179 ~~~{.js}
180 // JavaScript
181 // Image on 'disk' is 320x240.
182 var image = new dali.ResourceImage( { url:"flower.png", width:32, height:24});
183 // Image will be loaded at (32, 24), regardless of fitting mode.
184 ~~~
185
186
187 ### Quality Versus Speed and Memory Options {#resourceimagescalingsamplingmodes}
188
189 The process of scaling an image can be expensive in CPU cycles and add latency to the loading of each resource.
190 To allow the developer to trade-off speed against quality for different use cases, DALi provides the `SamplingMode` enum, which can be passed to `ResourceImage::New()`.
191 Two of these modes produce bitmaps which differ from the dimensions calculated by the fitting algorithm and so have a memory trade-off as well. The full set of modes is explained below.
192
193 | `SamplingMode` | **Operation** |
194 | ------------- | --------- |
195 | `NEAREST` | Use simple point sampling when scaling. For each pixel in output image, just one pixel is chosen from the input image. This is the fastest, crudest option but suffers the worst from aliasing artifacts so should only be used for fast previews, or where the source image is known to have very low-frequency features. |
196 | `LINEAR` | Uses a weighted bilinear filter with a `(2,2)` footprint when scaling. For each output pixel, four input pixels are averaged from the input image. This is a good quality option, equivalent to the GPU's filtering and works well at least down to a `0.5` scaling. |
197 | `BOX` | Uses an iterated `(2,2)` box filter to repeatedly halve the image in both dimensions, averaging adjacent pixels until the the result is approximately right for the fitting target rectangle. For each output pixel some number of pixels from the sequence `[4,16,64,256,1024,...]` are averaged from the input image, where the number averaged depends on the degree of scaling requested. This provides a very high quality result and is free from aliasing artifacts because of the iterated averaging. *The resulting bitmap will not exactly match the dimensions calculated by the fitting mode but it will be within a factor of two of it and have the same aspect ratio as it.*   |
198 | `BOX_THEN_NEAREST` | Applies the `BOX` mode to get within a factor of two of the fitted dimensions, and then finishes off with `NEAREST` to reach the exact dimensions. |
199 | `BOX_THEN_LINEAR` | Applies the `BOX` mode to get within a factor of two of the fitted dimensions, and then finishes off with `LINEAR` to reach the exact dimensions. This is the slowest option and of equivalent quality to `BOX`. It is superior to `BOX` in that is uses an average of 62% of the memory and exactly matches the dimensions calculated by fitting. **This is the best mode for most use cases**.  |
200 | `NO_FILTER` | Disables scaling altogether. In conjunction with `SCALE_TO_FILL` mode this can be useful as the edge trimming of that fitting mode is still applied. An example would be a gallery application, where a database of prescaled thumbnails of approximately the correct size need to be displayed in a regular grid of equal-sized cells, while being loaded at maximum speed. |
201
202 Here are all the modes applied to scaling-down a `(640,720)` line art and text JPEG image to a `(218, 227)` thumbnail:
203
204 |  |  | |
205 | ---- | ---- | --- |
206 | ![ ](../assets/img/image-scaling/sampling_modes_no_filter.png) ![ ](sampling_modes_no_filter.png) | ![ ](../assets/img/image-scaling/sampling_modes_nearest.png) ![ ](sampling_modes_nearest.png) | ![ ](../assets/img/image-scaling/sampling_modes_linear.png) ![ ](sampling_modes_linear.png) |
207 | **NO_FILTER** | **NEAREST** | **LINEAR** |
208 | ![ ](../assets/img/image-scaling/sampling_modes_box.png) ![ ](sampling_modes_box.png) | ![ ](../assets/img/image-scaling/sampling_modes_box_then_nearest.png) ![ ](sampling_modes_box_then_nearest.png) | ![ ](../assets/img/image-scaling/sampling_modes_box_then_linear.png) ![ ](sampling_modes_box_then_linear.png) |
209 | **BOX** | **BOX_THEN_NEAREST** | **BOX_THEN_LINEAR** |
210
211 These are screenshots, showing how the images are rendered in a DALi demo.
212 There is an additional level of GPU bilinear filtering happening at render time.
213 The best way to get a feel for the best sampling mode for different image types is to play with the [examples](#resourceimagescalingsamplingmodesexamples).
214
215 In the following code example the same image is loaded to be a thumbnail but with differing quality, speed, and memory implications.
216 ~~~{.cpp}
217 // C++
218 ResourceImage image1 = ResourceImage::New( "flower.png",
219     ImageDimensions( 240, 240 ), FittingMode::SCALE_TO_FILL, SamplingMode::NEAREST );
220
221 ResourceImage image2 = ResourceImage::New( "flower.png",
222     ImageDimensions( 240, 240 ), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER );
223
224 ResourceImage image3 = ResourceImage::New( "flower.png",
225     ImageDimensions( 240, 240 ), FittingMode::SCALE_TO_FILL, SamplingMode::BOX );
226
227 ResourceImage image4 = ResourceImage::New( "flower.png",
228     ImageDimensions( 240, 240 ), FittingMode::SCALE_TO_FILL, SamplingMode::BOX_THEN_LINEAR );
229 ~~~
230 ~~~{.js}
231 // JavaScript
232 var image1 = new dali.ResourceImage( {
233   url:"flower.png", width:240, height:240,
234   fitting-mode:"SCALE_TO_FILL", sampling-mode:"NEAREST"
235 } );
236
237 var image2 = new dali.ResourceImage( {
238   url:"flower.png", width:240, height:240,
239   fitting-mode:"SCALE_TO_FILL", sampling-mode:"NO_FILTER"
240 } );
241
242 var image3 = new dali.ResourceImage( {
243   url:"flower.png", width:240, height:240,
244   fitting-mode:"SCALE_TO_FILL", sampling-mode:"BOX"
245 } );
246
247 var image4 = new dali.ResourceImage( {
248   url:"flower.png", width:240, height:240,
249   fitting-mode:"SCALE_TO_FILL", sampling-mode:"BOX_THEN_LINEAR"
250 } );
251 ~~~
252
253 If we imagine flower.jpg is a 560*512 photo with high frequency details, the results of this are:
254 * `image1` loads fast, uses minimal space, has poor quality.
255 * `image2` loads even faster, uses 4.6 * minimal space, has good quality.
256 * `image3` loads moderately slow, uses 1.3 * minimal space, has good quality.
257 * `image4` loads slowest, uses minimal space, has good quality.
258
259 Note that `BOX`, `BOX_THEN_NEAREST` and `BOX_THEN_LINEAR` can work particularly well for JPEG images as they can use fast downscaling typically built-in to the JPEG codec on supported platforms on the fly while decoding. In this case the caveats about using them having a speed trade-off given above do not apply.
260
261 ## Passing a Zero Dimension {#resourceimagescalingzerodimensions}
262 Passing in a single zero dimension is effectively a shortcut for specifying `FIT_WIDTH` or `FIT_HEIGHT` `FittingMode`s. When a non-zero width and zero height are specified, the fitting done will be identical to the result using `FittingMode` `FIT_WIDTH`. When passing a zero width and non-zero height, the effect of applying the chosen `FittingMode` to the calculated target dimensions is always identical to applying the `FIT_HEIGHT` mode.
263
264 * `ResourceImage::New( ImageDimensions( x, 0 ), <ANY_FITTING_MODE> )` =
265   `ResourceImage::New( ImageDimensions( x, <ANYTHING> ), FittingMode::FIT_WIDTH )`
266 * `ResourceImage::New( ImageDimensions( 0, y ), <ANY_FITTING_MODE> )` =
267   `ResourceImage::New( ImageDimensions( <ANYTHING>, y), FittingMode::FIT_HEIGHT )`
268
269 This falls out of the the fact that the fitting modes modes are strategies for the case when the aspect ratio of the raw image differs from the aspect ratio of the target dimensions, but the zero dimension behavior always ensures that the target dimensions have the same aspect ratio as the raw image's so the fitting modes are all equivalent.
270
271 Therefore, if `(x!=0, y=0)`, fittingMode = `FIT_WIDTH`,
272 and if `(x=0, y=!0)`, fittingMode = `FIT_HEIGHT`, irrespective of fitting mode passed by the application (if any).
273 This shortcut is provided as a convenience to the developer and allows FIT_WIDTH or FIT_HEIGHT to be specified compactly:
274 ~~~{.cpp}
275 // C++
276 // FIT_WIDTH:
277 ResourceImage image = ResourceImage::New("flower.png", ImageDimensions(x, 0));
278 // FIT_HEIGHT:
279 ResourceImage image = ResourceImage::New("flower.png", ImageDimensions(0, y));
280 ~~~
281 ~~~{.js}
282 // JavaScript
283 // FIT_WIDTH:
284 var image = new dali.ResourceImage( {
285   url: "flower.png",
286   width: x,
287   height: 0
288 });
289 // FIT_HEIGHT:
290 var image = new dali.ResourceImage( {
291   url: "flower.png",
292   width: 0,
293   height: y
294 });
295 ~~~
296
297 ## Upscaling
298
299 DALi refuses to upscale images at load time in order to conserve memory.
300 If the application requests a size for an image that is larger than its raw dimensions, DALi will instead return an image with the same aspect ratio but limited to the largest dimensions that do not exceed the raw ones.
301 Upscaling can still be effected at render time by setting the size of an actor to the desired size.
302
303 ## Compressed Textures and Scaling
304
305 Compressed textures cannot be scaled at load time as their formats are designed to be uploaded directly to GPU memory. To achieve scaling of compressed textures, set the desired size on the attached `ImageActor` for scaling at render time instead.
306
307 ## Compensation for Natural Size != Pixel Width / Height {#resourceimagescalingsamplingmodesrendernaturalsize}
308
309 Because the *natural size* of an image is
310 [taken from the requested dimensions](#resourceimagescalingsamplingmodesdimensionflow)
311 passed to `ResourceImage::New()` rather than passing through the same calculations that result in the eventual pixel width and height loaded,
312 the *natural size* and pixel dimensions of an image will differ when loaded with scaling.
313 It is inherent in the definition of fitting modes other than `SCALE_TO_FILL` not to match the requested dimensions, so in general, images loaded with them must have this mismatch between *natural size* and actual pixel width.
314
315 It is not possible in general to draw a scaled resource image using its natural size as the `ImageActor`'s size without it appearing stretched in one dimension.
316 This is the case for example by default with size negotiation in effect or when an image is simply passed to an actor at creation time.
317
318 There are circumstance, however, in which the the natural size of a resource image loaded will exactly match its post-load pixel dimensions:
319
320 1. No scaling is requested.
321 1. The application chooses a combination of requested dimensions, fitting mode, and sampling mode which the scaling sub-system can match exactly. This is the case:
322    *  For all downscaling using `SCALE_TO_FILL` fitting mode and not using `BOX` or `NO_FILTER` sampling modes.
323    * The app uses `SHRINK_TO_FIT`, `FIT_WIDTH`, or `FIT_HEIGHT` and the requested dimensions passed-in are both smaller than the raw ones and have the same aspect ratio as them, and it is not using `BOX` or `NO_FILTER` sampling modes.
324
325 In these cases the image may be used freely in layouts controlled by size negotiation.
326 Additionally, if the requested size has the same aspect ratio as the eventual pixel array loaded, and the fitting mode is `SCALE_TO_FILL` or `BOX` and `NO_FILTER` sampling modes are avoided, even if they don't match in dimensions exactly, the eventual image will be drawn without aspect ratio distortion although it will be scaled at render time.
327
328 The fitting and scaling modes [demo](#resourceimagescalingsamplingmodesexamples) allows this behavior to be be explored dynamically when the fitting mode is changed from `SCALE_TO_FILL`.
329
330 The application can of course only pass dimensions which are just right if it happens to know the raw dimensions or if it accesses the the image resource and reads the raw dimensions from its header.
331
332 The application can get a scaled resource image rendered correctly to screen with one of three strategies:
333
334   1. Use one of the special cases above.
335   2. Read the image header from disk, recreate the dimension deriving, fitting, and sampling logic described in this document, and use that to generate a pair of requested dimensions which match the eventual image dimensions.
336   3. Use the requested dimensions it really wants to but then read the image header from disk, recreate the dimension deriving, fitting, and sampling logic described in this document, and set the size of an `ImageActor` to that size explicitly rather than relying on the *natural size* of the image.
337
338 ## Examples {#resourceimagescalingsamplingmodesexamples}
339 Load time image scaling is spread throughout the DALi examples.
340 Search for `"ImageDimensions"` in the dali-demo project to see it used.
341 There is also a specific demo to show all of the fitting and scaling modes.
342 which lives in the demo project at `examples/image-scaling-and-filtering`.
343
344 ![ ](../assets/img/image-scaling/demo-fitting-sampling.jpg) ![ ](./demo-fitting-sampling.jpg)
345
346 Touch the arrows in the top corners to changes image.
347 Drag the green button in the corner of the image to change the requested size and trigger an immediate image reload.
348 Use the buttons at the bottom of the screen to select any of the fitting and sampling modes from the popups which appear.
349 This demo does not take any of the special measures [described above](#resourceimagescalingsamplingmodesrendernaturalsize) to correct for the natural size != pixel dimensions discrepancy so all fitting modes other than `SCALE_TO_FILL` show distortion.
350
351 A second specific demo shows the effect of a filter mode on a single image loaded into various requested rectangles side by side.
352 It can be found under `examples/image-scaling-irregular-grid`.
353
354 ![ ](../assets/img/image-scaling/demo-sampling-modes.jpg) ![ ](./demo-sampling-modes.jpg)
355
356 Touch the button at top-left to change image.
357 The button at top-right changes sampling mode.
358 You will see strong differences between sampling modes where the image contains high frequency details such as hair and in the large black and white image, but much less in some others such as the Statue of Liberty which is mostly covered by a smooth gradient.
359
360 @class _Guide_Resource_Image_Scaling
361 */