Antialiasing improvements.
This is in response to Issue #832. The previous FXAA algorithm didn't work particularly well unless applied to upsampled outputs. This implements a slighltly more complex algorithm (same as molstar) that looks better without upscaling. Add an upscale viewer config option so user can configure level of refinement.
@@ -27,9 +27,8 @@
|
|||||||
ga('send', 'pageview');
|
ga('send', 'pageview');
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script type="module">
|
<script src="./build/3Dmol.js"></script>
|
||||||
import * as $3Dmol from './build/3Dmol.es6.js';
|
<script>
|
||||||
|
|
||||||
const initShapes = function (viewer) {
|
const initShapes = function (viewer) {
|
||||||
$.get('tests/test_structs/benzene-homo.cube', function (data) {
|
$.get('tests/test_structs/benzene-homo.cube', function (data) {
|
||||||
var voldata = new $3Dmol.VolumeData(data, "cube");
|
var voldata = new $3Dmol.VolumeData(data, "cube");
|
||||||
|
|||||||
@@ -167,23 +167,14 @@ export class GLViewer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private setupRenderer() {
|
private setupRenderer() {
|
||||||
|
let rendopt = {...this.config,
|
||||||
this.renderer = new Renderer({
|
|
||||||
antialias: this.config.antialias,
|
|
||||||
preserveDrawingBuffer: true, //so we can export images
|
preserveDrawingBuffer: true, //so we can export images
|
||||||
premultipliedAlpha: false,/* more traditional compositing with background */
|
premultipliedAlpha: false,/* more traditional compositing with background */
|
||||||
id: this.config.id,
|
|
||||||
row: this.config.row,
|
|
||||||
col: this.config.col,
|
|
||||||
rows: this.config.rows,
|
|
||||||
cols: this.config.cols,
|
|
||||||
canvas: this.config.canvas,
|
|
||||||
//cannot initialize with zero size - render will start out lost
|
//cannot initialize with zero size - render will start out lost
|
||||||
containerWidth: this.WIDTH,
|
containerWidth: this.WIDTH,
|
||||||
containerHeight: this.HEIGHT,
|
containerHeight: this.HEIGHT
|
||||||
ambientOcclusion: this.config.ambientOcclusion,
|
};
|
||||||
outline: this.config.outline
|
this.renderer = new Renderer(rendopt);
|
||||||
});
|
|
||||||
this.renderer.domElement.style.width = "100%";
|
this.renderer.domElement.style.width = "100%";
|
||||||
this.renderer.domElement.style.height = "100%";
|
this.renderer.domElement.style.height = "100%";
|
||||||
this.renderer.domElement.style.padding = "0";
|
this.renderer.domElement.style.padding = "0";
|
||||||
@@ -4486,7 +4477,7 @@ export class GLViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sync = !!(syncSurface);
|
var sync = !!(syncSurface);
|
||||||
if (typeof $3Dmol == "undefined" || !!$3Dmol.SurfaceWorker) {
|
if (typeof $3Dmol == "undefined" || typeof $3Dmol.SurfaceWorker == "undefined") {
|
||||||
console.log(
|
console.log(
|
||||||
"$3Dmol.SurfaceWorker is not defined, using synchronous surface generation.",
|
"$3Dmol.SurfaceWorker is not defined, using synchronous surface generation.",
|
||||||
);
|
);
|
||||||
@@ -4522,6 +4513,7 @@ export class GLViewer {
|
|||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
surfobj.done = true;
|
surfobj.done = true;
|
||||||
|
self.render(); // for consistency with parallel case, call render when done
|
||||||
return Promise.resolve(surfid);
|
return Promise.resolve(surfid);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -5149,7 +5141,7 @@ export interface ViewerSpec {
|
|||||||
hoverDuration?: number;
|
hoverDuration?: number;
|
||||||
/** id of the canvas */
|
/** id of the canvas */
|
||||||
id?: string;
|
id?: string;
|
||||||
/** default 5 */
|
/** default 10 */
|
||||||
cartoonQuality?: number;
|
cartoonQuality?: number;
|
||||||
/** */
|
/** */
|
||||||
row?: number;
|
row?: number;
|
||||||
@@ -5168,11 +5160,13 @@ export interface ViewerSpec {
|
|||||||
lowerZoomLimit?: number;
|
lowerZoomLimit?: number;
|
||||||
/** */
|
/** */
|
||||||
upperZoomLimit?: number;
|
upperZoomLimit?: number;
|
||||||
/** */
|
/** Enable antialiasing */
|
||||||
antialias?: boolean;
|
antialias?: boolean;
|
||||||
|
/** Render upscaled to 2x resolution. Defaults to antialiasing setting. Ignored for Retina displays. */
|
||||||
|
upscale?: boolean;
|
||||||
/** */
|
/** */
|
||||||
control_all?: boolean;
|
control_all?: boolean;
|
||||||
/** */
|
/** Orthographic instead of perspective rendering. Default false. */
|
||||||
orthographic?: boolean;
|
orthographic?: boolean;
|
||||||
/** Disable fog, default to false */
|
/** Disable fog, default to false */
|
||||||
disableFog?: boolean;
|
disableFog?: boolean;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
//Hackish way to create webworker (independent of $3Dmol namespace) within minified file
|
//Hackish way to create webworker (independent of $3Dmol namespace) within minified file
|
||||||
//We need to convert actual javascript into a string, not typescript, so for the time being
|
//We need to convert actual javascript into a string, not typescript, so for the time being
|
||||||
|
|
||||||
//this will remain a JS file
|
//this will remain a JS file
|
||||||
$3Dmol.workerString = function(){
|
$3Dmol.workerString = function(){
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,7 @@ export class Renderer {
|
|||||||
private _alpha: any;
|
private _alpha: any;
|
||||||
private _premultipliedAlpha: any;
|
private _premultipliedAlpha: any;
|
||||||
private _antialias: any;
|
private _antialias: any;
|
||||||
|
private _upscale: boolean | null = null;
|
||||||
private _preserveDrawingBuffer: any;
|
private _preserveDrawingBuffer: any;
|
||||||
private _clearColor: Color;
|
private _clearColor: Color;
|
||||||
private _clearAlpha: any;
|
private _clearAlpha: any;
|
||||||
@@ -178,6 +179,7 @@ export class Renderer {
|
|||||||
? parameters.premultipliedAlpha
|
? parameters.premultipliedAlpha
|
||||||
: true;
|
: true;
|
||||||
this._antialias = parameters.antialias !== undefined ? parameters.antialias : false;
|
this._antialias = parameters.antialias !== undefined ? parameters.antialias : false;
|
||||||
|
this._upscale = parameters.upscale !== undefined ? parameters.upscale : this._antialias;
|
||||||
|
|
||||||
this._preserveDrawingBuffer =
|
this._preserveDrawingBuffer =
|
||||||
parameters.preserveDrawingBuffer !== undefined
|
parameters.preserveDrawingBuffer !== undefined
|
||||||
@@ -324,9 +326,9 @@ export class Renderer {
|
|||||||
//zooming (in the browser) changes the pixel ratio and width/height
|
//zooming (in the browser) changes the pixel ratio and width/height
|
||||||
this.devicePixelRatio =
|
this.devicePixelRatio =
|
||||||
window.devicePixelRatio !== undefined ? window.devicePixelRatio : 1;
|
window.devicePixelRatio !== undefined ? window.devicePixelRatio : 1;
|
||||||
//with antialiasing on (which doesn't seem to do much), render at double rsolution to eliminate jaggies
|
//with antialiasing on, render at double rsolution to eliminate jaggies
|
||||||
//my iphone crashes if we do though, so as a hacky workaround, don't do it with retina displays
|
//don't do it with high resolution displays
|
||||||
if (this._antialias && this.devicePixelRatio < 2.0) this.devicePixelRatio *= 2.0;
|
if (this._upscale && this.devicePixelRatio < 2.0) this.devicePixelRatio = 2.0;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.rows != undefined &&
|
this.rows != undefined &&
|
||||||
|
|||||||
@@ -1,3 +1,242 @@
|
|||||||
|
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
precision highp int;
|
||||||
|
precision highp sampler2D;
|
||||||
|
|
||||||
|
uniform sampler2D tColor;
|
||||||
|
varying highp vec2 vTexCoords;
|
||||||
|
|
||||||
|
// adapted from https://github.com/kosua20/Rendu
|
||||||
|
// MIT License Copyright (c) 2017 Simon Rodriguez
|
||||||
|
// by way of molstar (https://github.com/molstar/molstar/blob/master/src/mol-gl/shader/fxaa.frag.ts)
|
||||||
|
|
||||||
|
#define QUALITY(q) ((q) < 5 ? 1.0 : ((q) > 5 ? ((q) < 10 ? 2.0 : ((q) < 11 ? 4.0 : 8.0)) : 1.5))
|
||||||
|
|
||||||
|
float rgb2luma(vec3 rgb){
|
||||||
|
return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114)));
|
||||||
|
}
|
||||||
|
|
||||||
|
float sampleLuma(vec2 uv) {
|
||||||
|
return rgb2luma(texture2D(tColor, uv).rgb);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sampleLuma(vec2 uv, float uOffset, float vOffset) {
|
||||||
|
uv += vec2(uOffset, vOffset);
|
||||||
|
return sampleLuma(uv);
|
||||||
|
}
|
||||||
|
|
||||||
|
//DEFINEFRAGCOLOR
|
||||||
|
void main(void) {
|
||||||
|
ivec2 dim = textureSize(tColor,0);
|
||||||
|
vec2 dimensions = vec2(float(dim.x),float(dim.y));
|
||||||
|
vec2 inverseScreenSize = vec2(1.0 / dimensions.x, 1.0 / dimensions.y);
|
||||||
|
vec2 coords = vTexCoords;
|
||||||
|
|
||||||
|
vec4 colorCenter = texture2D(tColor, coords);
|
||||||
|
float dEdgeThresholdMin = 0.0312;
|
||||||
|
float dEdgeThresholdMax = 0.125;
|
||||||
|
int dIterations = 12;
|
||||||
|
float dSubpixelQuality = 0.3;
|
||||||
|
|
||||||
|
// Luma at the current fragment
|
||||||
|
float lumaCenter = rgb2luma(colorCenter.rgb);
|
||||||
|
|
||||||
|
// Luma at the four direct neighbours of the current fragment.
|
||||||
|
float lumaDown = sampleLuma(coords, 0.0, -inverseScreenSize.y);
|
||||||
|
float lumaUp = sampleLuma(coords, 0.0, inverseScreenSize.y);
|
||||||
|
float lumaLeft = sampleLuma(coords, -inverseScreenSize.x, 0.0);
|
||||||
|
float lumaRight = sampleLuma(coords, inverseScreenSize.x, 0.0);
|
||||||
|
|
||||||
|
// Find the maximum and minimum luma around the current fragment.
|
||||||
|
float lumaMin = min(lumaCenter, min(min(lumaDown, lumaUp), min(lumaLeft, lumaRight)));
|
||||||
|
float lumaMax = max(lumaCenter, max(max(lumaDown, lumaUp), max(lumaLeft, lumaRight)));
|
||||||
|
|
||||||
|
// Compute the delta.
|
||||||
|
float lumaRange = lumaMax - lumaMin;
|
||||||
|
|
||||||
|
// If the luma variation is lower that a threshold (or if we are in a really dark area),
|
||||||
|
// we are not on an edge, don't perform any AA.
|
||||||
|
if (lumaRange < max(dEdgeThresholdMin, lumaMax * dEdgeThresholdMax)) {
|
||||||
|
gl_FragColor = colorCenter;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query the 4 remaining corners lumas.
|
||||||
|
float lumaDownLeft = sampleLuma(coords, -inverseScreenSize.x, -inverseScreenSize.y);
|
||||||
|
float lumaUpRight = sampleLuma(coords, inverseScreenSize.x, inverseScreenSize.y);
|
||||||
|
float lumaUpLeft = sampleLuma(coords, -inverseScreenSize.x, inverseScreenSize.y);
|
||||||
|
float lumaDownRight = sampleLuma(coords, inverseScreenSize.x, -inverseScreenSize.y);
|
||||||
|
|
||||||
|
// Combine the four edges lumas (using intermediary variables for future computations
|
||||||
|
// with the same values).
|
||||||
|
float lumaDownUp = lumaDown + lumaUp;
|
||||||
|
float lumaLeftRight = lumaLeft + lumaRight;
|
||||||
|
|
||||||
|
// Same for corners
|
||||||
|
float lumaLeftCorners = lumaDownLeft + lumaUpLeft;
|
||||||
|
float lumaDownCorners = lumaDownLeft + lumaDownRight;
|
||||||
|
float lumaRightCorners = lumaDownRight + lumaUpRight;
|
||||||
|
float lumaUpCorners = lumaUpRight + lumaUpLeft;
|
||||||
|
|
||||||
|
// Compute an estimation of the gradient along the horizontal and vertical axis.
|
||||||
|
float edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + abs(-2.0 * lumaCenter + lumaDownUp) * 2.0 + abs(-2.0 * lumaRight + lumaRightCorners);
|
||||||
|
float edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + abs(-2.0 * lumaDown + lumaDownCorners);
|
||||||
|
|
||||||
|
// Is the local edge horizontal or vertical ?
|
||||||
|
bool isHorizontal = (edgeHorizontal >= edgeVertical);
|
||||||
|
|
||||||
|
// Choose the step size (one pixel) accordingly.
|
||||||
|
float stepLength = isHorizontal ? inverseScreenSize.y : inverseScreenSize.x;
|
||||||
|
|
||||||
|
// Select the two neighboring texels lumas in the opposite direction to the local edge.
|
||||||
|
float luma1 = isHorizontal ? lumaDown : lumaLeft;
|
||||||
|
float luma2 = isHorizontal ? lumaUp : lumaRight;
|
||||||
|
// Compute gradients in this direction.
|
||||||
|
float gradient1 = luma1 - lumaCenter;
|
||||||
|
float gradient2 = luma2 - lumaCenter;
|
||||||
|
|
||||||
|
// Which direction is the steepest ?
|
||||||
|
bool is1Steepest = abs(gradient1) >= abs(gradient2);
|
||||||
|
|
||||||
|
// Gradient in the corresponding direction, normalized.
|
||||||
|
float gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2));
|
||||||
|
|
||||||
|
// Average luma in the correct direction.
|
||||||
|
float lumaLocalAverage = 0.0;
|
||||||
|
if(is1Steepest){
|
||||||
|
// Switch the direction
|
||||||
|
stepLength = -stepLength;
|
||||||
|
lumaLocalAverage = 0.5 * (luma1 + lumaCenter);
|
||||||
|
} else {
|
||||||
|
lumaLocalAverage = 0.5 * (luma2 + lumaCenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift UV in the correct direction by half a pixel.
|
||||||
|
vec2 currentUv = coords;
|
||||||
|
if(isHorizontal){
|
||||||
|
currentUv.y += stepLength * 0.5;
|
||||||
|
} else {
|
||||||
|
currentUv.x += stepLength * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute offset (for each iteration step) in the right direction.
|
||||||
|
vec2 offset = isHorizontal ? vec2(inverseScreenSize.x, 0.0) : vec2(0.0, inverseScreenSize.y);
|
||||||
|
// Compute UVs to explore on each side of the edge, orthogonally.
|
||||||
|
// The QUALITY allows us to step faster.
|
||||||
|
vec2 uv1 = currentUv - offset * QUALITY(0);
|
||||||
|
vec2 uv2 = currentUv + offset * QUALITY(0);
|
||||||
|
|
||||||
|
// Read the lumas at both current extremities of the exploration segment,
|
||||||
|
// and compute the delta wrt to the local average luma.
|
||||||
|
float lumaEnd1 = sampleLuma(uv1);
|
||||||
|
float lumaEnd2 = sampleLuma(uv2);
|
||||||
|
lumaEnd1 -= lumaLocalAverage;
|
||||||
|
lumaEnd2 -= lumaLocalAverage;
|
||||||
|
|
||||||
|
// If the luma deltas at the current extremities is larger than the local gradient,
|
||||||
|
// we have reached the side of the edge.
|
||||||
|
bool reached1 = abs(lumaEnd1) >= gradientScaled;
|
||||||
|
bool reached2 = abs(lumaEnd2) >= gradientScaled;
|
||||||
|
bool reachedBoth = reached1 && reached2;
|
||||||
|
|
||||||
|
// If the side is not reached, we continue to explore in this direction.
|
||||||
|
if(!reached1){
|
||||||
|
uv1 -= offset * QUALITY(1);
|
||||||
|
}
|
||||||
|
if(!reached2){
|
||||||
|
uv2 += offset * QUALITY(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If both sides have not been reached, continue to explore.
|
||||||
|
if(!reachedBoth){
|
||||||
|
for(int i = 2; i < dIterations; i++){
|
||||||
|
// If needed, read luma in 1st direction, compute delta.
|
||||||
|
if(!reached1){
|
||||||
|
lumaEnd1 = sampleLuma(uv1);
|
||||||
|
lumaEnd1 = lumaEnd1 - lumaLocalAverage;
|
||||||
|
}
|
||||||
|
// If needed, read luma in opposite direction, compute delta.
|
||||||
|
if(!reached2){
|
||||||
|
lumaEnd2 = sampleLuma(uv2);
|
||||||
|
lumaEnd2 = lumaEnd2 - lumaLocalAverage;
|
||||||
|
}
|
||||||
|
// If the luma deltas at the current extremities is larger than the local gradient,
|
||||||
|
// we have reached the side of the edge.
|
||||||
|
reached1 = abs(lumaEnd1) >= gradientScaled;
|
||||||
|
reached2 = abs(lumaEnd2) >= gradientScaled;
|
||||||
|
reachedBoth = reached1 && reached2;
|
||||||
|
|
||||||
|
// If the side is not reached, we continue to explore in this direction,
|
||||||
|
// with a variable quality.
|
||||||
|
if(!reached1){
|
||||||
|
uv1 -= offset * QUALITY(i);
|
||||||
|
}
|
||||||
|
if(!reached2){
|
||||||
|
uv2 += offset * QUALITY(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If both sides have been reached, stop the exploration.
|
||||||
|
if(reachedBoth){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the distances to each side edge of the edge (!).
|
||||||
|
float distance1 = isHorizontal ? (coords.x - uv1.x) : (coords.y - uv1.y);
|
||||||
|
float distance2 = isHorizontal ? (uv2.x - coords.x) : (uv2.y - coords.y);
|
||||||
|
|
||||||
|
// In which direction is the side of the edge closer ?
|
||||||
|
bool isDirection1 = distance1 < distance2;
|
||||||
|
float distanceFinal = min(distance1, distance2);
|
||||||
|
|
||||||
|
// Thickness of the edge.
|
||||||
|
float edgeThickness = (distance1 + distance2);
|
||||||
|
|
||||||
|
// Is the luma at center smaller than the local average ?
|
||||||
|
bool isLumaCenterSmaller = lumaCenter < lumaLocalAverage;
|
||||||
|
|
||||||
|
// If the luma at center is smaller than at its neighbour,
|
||||||
|
// the delta luma at each end should be positive (same variation).
|
||||||
|
bool correctVariation1 = (lumaEnd1 < 0.0) != isLumaCenterSmaller;
|
||||||
|
bool correctVariation2 = (lumaEnd2 < 0.0) != isLumaCenterSmaller;
|
||||||
|
|
||||||
|
// Only keep the result in the direction of the closer side of the edge.
|
||||||
|
bool correctVariation = isDirection1 ? correctVariation1 : correctVariation2;
|
||||||
|
|
||||||
|
// UV offset: read in the direction of the closest side of the edge.
|
||||||
|
float pixelOffset = - distanceFinal / edgeThickness + 0.5;
|
||||||
|
|
||||||
|
// If the luma variation is incorrect, do not offset.
|
||||||
|
float finalOffset = correctVariation ? pixelOffset : 0.0;
|
||||||
|
|
||||||
|
// Sub-pixel shifting
|
||||||
|
// Full weighted average of the luma over the 3x3 neighborhood.
|
||||||
|
float lumaAverage = (1.0 / 12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners);
|
||||||
|
// Ratio of the delta between the global average and the center luma,
|
||||||
|
// over the luma range in the 3x3 neighborhood.
|
||||||
|
float subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0);
|
||||||
|
float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;
|
||||||
|
// Compute a sub-pixel offset based on this delta.
|
||||||
|
float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * float(dSubpixelQuality);
|
||||||
|
|
||||||
|
// Pick the biggest of the two offsets.
|
||||||
|
finalOffset = max(finalOffset, subPixelOffsetFinal);
|
||||||
|
|
||||||
|
// Compute the final UV coordinates.
|
||||||
|
vec2 finalUv = coords;
|
||||||
|
if(isHorizontal){
|
||||||
|
finalUv.y += finalOffset * stepLength;
|
||||||
|
} else {
|
||||||
|
finalUv.x += finalOffset * stepLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the color at the new UV coordinates, and use it.
|
||||||
|
gl_FragColor = texture2D(tColor, finalUv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* old fxaa implementation
|
||||||
uniform highp sampler2D colormap;
|
uniform highp sampler2D colormap;
|
||||||
varying highp vec2 vTexCoords;
|
varying highp vec2 vTexCoords;
|
||||||
|
|
||||||
@@ -63,4 +302,5 @@ void main (void) {
|
|||||||
|
|
||||||
gl_FragColor = applyFXAA(vTexCoords, colormap);
|
gl_FragColor = applyFXAA(vTexCoords, colormap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
11
tests/auto/tests/antialias.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
@div
|
||||||
|
<div style="height: 600px; width: 600px;" class='viewer_3Dmoljs'
|
||||||
|
data-config='antialias:false;upscale:false' data-pdb='3M8L' data-backgroundcolor='0xffffff'
|
||||||
|
data-select1='chain:A' data-style1='stick'
|
||||||
|
data-select2='chain:B' data-style2='sphere'
|
||||||
|
data-select3='chain:C' data-style3='cartoon'></div>
|
||||||
|
|
||||||
|
*/
|
||||||
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 275 KiB |
|
Before Width: | Height: | Size: 371 KiB After Width: | Height: | Size: 334 KiB |
|
Before Width: | Height: | Size: 284 KiB After Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 231 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 169 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 296 KiB After Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 582 KiB After Width: | Height: | Size: 527 KiB |
|
Before Width: | Height: | Size: 357 KiB After Width: | Height: | Size: 231 KiB |
|
Before Width: | Height: | Size: 292 KiB After Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 778 KiB After Width: | Height: | Size: 614 KiB |
|
Before Width: | Height: | Size: 349 KiB After Width: | Height: | Size: 325 KiB |
|
Before Width: | Height: | Size: 405 KiB After Width: | Height: | Size: 378 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 121 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 217 KiB |
|
Before Width: | Height: | Size: 281 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 290 KiB After Width: | Height: | Size: 273 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 498 KiB After Width: | Height: | Size: 459 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 498 KiB After Width: | Height: | Size: 453 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 486 KiB After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 842 KiB After Width: | Height: | Size: 768 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 292 KiB After Width: | Height: | Size: 272 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 762 KiB After Width: | Height: | Size: 692 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 186 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 627 KiB After Width: | Height: | Size: 578 KiB |
|
Before Width: | Height: | Size: 345 KiB After Width: | Height: | Size: 312 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 365 KiB After Width: | Height: | Size: 335 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 292 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 575 KiB After Width: | Height: | Size: 516 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 236 KiB After Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 319 KiB After Width: | Height: | Size: 301 KiB |
|
Before Width: | Height: | Size: 257 KiB After Width: | Height: | Size: 234 KiB |
|
Before Width: | Height: | Size: 188 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 303 KiB After Width: | Height: | Size: 294 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 156 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 377 KiB After Width: | Height: | Size: 361 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 397 KiB After Width: | Height: | Size: 367 KiB |
|
Before Width: | Height: | Size: 360 KiB After Width: | Height: | Size: 351 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 324 KiB After Width: | Height: | Size: 317 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 315 KiB After Width: | Height: | Size: 300 KiB |