Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/css/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import {paintOrder} from './property-descriptors/paint-order';
import {webkitTextStrokeColor} from './property-descriptors/webkit-text-stroke-color';
import {webkitTextStrokeWidth} from './property-descriptors/webkit-text-stroke-width';
import {Context} from '../core/context';
import {objectFit} from './property-descriptors/object-fit';

export class CSSParsedDeclaration {
animationDuration: ReturnType<typeof duration.parse>;
Expand Down Expand Up @@ -130,6 +131,7 @@ export class CSSParsedDeclaration {
overflowX: OVERFLOW;
overflowY: OVERFLOW;
overflowWrap: ReturnType<typeof overflowWrap.parse>;
objectFit: ReturnType<typeof objectFit.parse>;
paddingTop: LengthPercentage;
paddingRight: LengthPercentage;
paddingBottom: LengthPercentage;
Expand Down Expand Up @@ -199,6 +201,7 @@ export class CSSParsedDeclaration {
this.overflowX = overflowTuple[0];
this.overflowY = overflowTuple[overflowTuple.length > 1 ? 1 : 0];
this.overflowWrap = parse(context, overflowWrap, declaration.overflowWrap);
this.objectFit = parse(context, objectFit, declaration.objectFit);
this.paddingTop = parse(context, paddingTop, declaration.paddingTop);
this.paddingRight = parse(context, paddingRight, declaration.paddingRight);
this.paddingBottom = parse(context, paddingBottom, declaration.paddingBottom);
Expand Down
58 changes: 58 additions & 0 deletions src/css/property-descriptors/object-fit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import {Context} from '../../core/context';
import {IPropertyIdentValueDescriptor, PropertyDescriptorParsingType} from '../IPropertyDescriptor';
export const enum OBJECT_FIT {
CONTAIN = 'contain',
COVER = 'cover',
FILL = 'fill'
}

export const objectFit: IPropertyIdentValueDescriptor<OBJECT_FIT> = {
name: 'object-fit',
initialValue: 'fill',
prefix: false,
type: PropertyDescriptorParsingType.IDENT_VALUE,
parse: function (_context: Context, token: string) {
switch (token) {
case 'contain':
return OBJECT_FIT.CONTAIN;
case 'cover':
return OBJECT_FIT.COVER;
default:
return OBJECT_FIT.FILL;
}
}
};

export const getObjectFitSize = (
contains: boolean /* true = contain, false = cover */,
containerWidth: number,
containerHeight: number,
width: number,
height: number
): {
width: number;
height: number;
x: number;
y: number;
} => {
const doRatio = width / height;
const cRatio = containerWidth / containerHeight;
let targetWidth = 0;
let targetHeight = 0;
const test = contains ? doRatio > cRatio : doRatio < cRatio;

if (test) {
targetWidth = containerWidth;
targetHeight = targetWidth / doRatio;
} else {
targetHeight = containerHeight;
targetWidth = targetHeight * doRatio;
}

return {
width: targetWidth,
height: targetHeight,
x: (containerWidth - targetWidth) / 2,
y: (containerHeight - targetHeight) / 2
};
};
36 changes: 33 additions & 3 deletions src/dom/document-cloner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {CSSParsedCounterDeclaration, CSSParsedPseudoDeclaration} from '../css/in
import {getQuote} from '../css/property-descriptors/quotes';
import {Context} from '../core/context';
import {DebuggerType, isDebugging} from '../core/debugger';
import {getObjectFitSize, OBJECT_FIT, objectFit} from '../css/property-descriptors/object-fit';

export interface CloneOptions {
ignoreElements?: (element: Element) => boolean;
Expand Down Expand Up @@ -256,9 +257,38 @@ export class DocumentCloner {

try {
if (ctx) {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
if (!this.options.allowTaint) {
ctx.getImageData(0, 0, canvas.width, canvas.height);
const elObjectFit = objectFit.parse(
this.context,
video.ownerDocument.defaultView?.getComputedStyle(video).objectFit ?? ''
);
if (elObjectFit == OBJECT_FIT.CONTAIN || elObjectFit == OBJECT_FIT.COVER) {
const dimension = getObjectFitSize(
elObjectFit == OBJECT_FIT.CONTAIN,
video.offsetWidth,
video.offsetHeight,
video.videoWidth,
video.videoHeight
);

ctx.drawImage(
video,
0,
0,
video.videoWidth,
video.videoHeight,
dimension.x,
dimension.y,
dimension.width,
dimension.height
);
if (!this.options.allowTaint) {
ctx.getImageData(0, 0, canvas.width, canvas.height);
}
} else {
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
if (!this.options.allowTaint) {
ctx.getImageData(0, 0, canvas.width, canvas.height);
}
}
}
return canvas;
Expand Down
53 changes: 42 additions & 11 deletions src/render/canvas/canvas-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {PAINT_ORDER_LAYER} from '../../css/property-descriptors/paint-order';
import {Renderer} from '../renderer';
import {Context} from '../../core/context';
import {DIRECTION} from '../../css/property-descriptors/direction';
import {getObjectFitSize} from '../../css/property-descriptors/object-fit';

export type RenderConfigurations = RenderOptions & {
backgroundColor: Color | null;
Expand Down Expand Up @@ -270,23 +271,53 @@ export class CanvasRenderer extends Renderer {
curves: BoundCurves,
image: HTMLImageElement | HTMLCanvasElement
): void {
const objectFit = container.styles.objectFit;
if (image && container.intrinsicWidth > 0 && container.intrinsicHeight > 0) {
const box = contentBox(container);
const path = calculatePaddingBoxPath(curves);
this.path(path);
this.ctx.save();
this.ctx.clip();
this.ctx.drawImage(
image,
0,
0,
container.intrinsicWidth,
container.intrinsicHeight,
box.left,
box.top,
box.width,
box.height
);

let newWidth;
let newHeight;
const newX = box.left;
const newY = box.top;

if (objectFit === 'contain' || objectFit === 'cover') {
const {width, height, x, y} = getObjectFitSize(
objectFit === 'contain',
box.width,
box.height,
image.width,
image.height
);
newWidth = width;
newHeight = height;
this.ctx.drawImage(
image,
0,
0,
container.intrinsicWidth,
container.intrinsicHeight,
newX + x,
newY + y,
newWidth,
newHeight
);
} else {
this.ctx.drawImage(
image,
0,
0,
container.intrinsicWidth,
container.intrinsicHeight,
box.left,
box.top,
box.width,
box.height
);
}
this.ctx.restore();
}
}
Expand Down
4 changes: 4 additions & 0 deletions tests/reftests/images/images.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@

<img srcset="../../assets/image.jpg, ../../assets/image2.jpg 2x" src="../../assets/image.jpg" style="width: 75px">

<img src="../../assets/image.jpg" style="width:50px;height:400px;object-fit:contain" />
<img src="../../assets/image.jpg" style="width:50px;height:400px;object-fit:cover" />
<img src="../../assets/image.jpg" style="width:400px;height:50px;object-fit:contain" />
<img src="../../assets/image.jpg" style="width:400px;height:50px;object-fit:cover" />
<script>
const base64 = "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";
const bytes = atob(base64);
Expand Down
16 changes: 16 additions & 0 deletions tests/reftests/images/video.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@ <h2>Same origin</h2>
<source src="../../assets/cc0-video.mp4" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
<video controls style="width: 50px; height: 400px; object-fit: contain;">
<source src="../../assets/cc0-video.mp4" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
<video controls style="width: 50px; height: 400px; object-fit: cover;">
<source src="../../assets/cc0-video.mp4" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
<video controls style="width: 400px; height: 50px; object-fit: contain;">
<source src="../../assets/cc0-video.mp4" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
<video controls style="width: 400px; height: 50px; object-fit: cover;">
<source src="../../assets/cc0-video.mp4" type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>
<h2>Cross-origin (doesn't taint)</h2>
<video controls width="250">
<source src="http://localhost:8081/cors/tests/assets/cc0-video.mp4" type="video/mp4">
Expand Down