Skip to content

Commit

Permalink
Added annotate to MagickImage.
Browse files Browse the repository at this point in the history
  • Loading branch information
dlemstra committed Jan 5, 2025
1 parent da9a5c6 commit ee5a20b
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
5 changes: 4 additions & 1 deletion src/internal/native/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export function _withNativeString<TReturnType>(api: ImageMagickApi, str: string,
}

/** @internal */
export function _withString<TReturnType>(str: string, func: (instance: number) => TReturnType): TReturnType {
export function _withString<TReturnType>(str: string | null, func: (instance: number) => TReturnType): TReturnType {
if (str === null)
return func(0);

return _withNativeString(ImageMagick._api, str, func);
}
73 changes: 73 additions & 0 deletions src/magick-image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { Disposable } from './internal/disposable';
import { DisposableArray } from './internal/disposable-array';
import { DistortMethod } from './enums/distort-method';
import { DistortSettings } from './settings/distort-settings';
import { DrawingSettings } from './internal/settings/drawing-settings';
import { DrawingWand } from './drawing/drawing-wand';
import { DoublePointer } from './internal/pointer/double-pointer';
import { Endian } from './enums/endian';
Expand Down Expand Up @@ -442,6 +443,45 @@ export interface IMagickImage extends IDisposable {
*/
alpha(value: AlphaOption): void;

/**
* Annotate using specified text, and bounding area.
* @param text The text to use.
* @param boundingArea The bounding area.
*/
annotate(text: string, boundingArea: MagickGeometry): void;

/**
* Annotate using specified text, bounding area, and placement gravity.
* @param text The text to use.
* @param boundingArea The bounding area.
* @param gravity The gravity to use.
*/
annotate(text: string, boundingArea: MagickGeometry, gravity: Gravity): void;

/**
* Annotate using specified text, bounding area, and placement gravity.
* @param text The text to use.
* @param boundingArea The bounding area.
* @param gravity The gravity to use.
* @param angle The rotation.
*/
annotate(text: string, boundingArea: MagickGeometry, gravity: Gravity, angle: number): void;

/**
* Annotate with text (bounding area is entire image) and placement gravity.
* @param text The text to use.
* @param gravity The gravity to use.
*/
annotate(text: string, gravity: Gravity): void;

/**
* Annotate with text (bounding area is entire image) and placement gravity.
* @param text The text to use.
* @param gravity The gravity to use.
* @param angle The rotation.
*/
annotate(text: string, gravity: Gravity, angle: number): void;

/**
* Extracts the 'mean' from the image and adjust the image to try make set its gamma appropriately.
*/
Expand Down Expand Up @@ -2278,6 +2318,39 @@ export class MagickImage extends NativeInstance implements IMagickImage {
});
}

annotate(text: string, boundingArea: MagickGeometry): void;
annotate(text: string, boundingArea: MagickGeometry, gravity: Gravity): void;
annotate(text: string, boundingArea: MagickGeometry, gravity: Gravity, angle: number): void;
annotate(text: string, gravity: Gravity): void;
annotate(text: string, gravity: Gravity, angle: number): void;
annotate(text: string, boundingAreaOrGravity: MagickGeometry | Gravity, gravityOrAngleOrUndefined?: Gravity | number, angleOrUndefined?: number): void {
const drawingSettings = DrawingSettings._create(this._settings);
return this.useExceptionPointer(exception => {
return drawingSettings._use(settings => {
_withString(text, textPtr => {
let boundingArea: string | null = null;
let gravity = Gravity.Undefined;
let angle = 0.0;
if (typeof boundingAreaOrGravity === 'object') {
boundingArea = boundingAreaOrGravity.toString();
if (gravityOrAngleOrUndefined !== undefined)
gravity = <Gravity>gravityOrAngleOrUndefined;
if (angleOrUndefined !== undefined)
angle = angleOrUndefined;
} else {
gravity = boundingAreaOrGravity;
if (gravityOrAngleOrUndefined !== undefined)
angle = <number>gravityOrAngleOrUndefined;
}

_withString(boundingArea, boundingAreaPtr => {
ImageMagick._api._MagickImage_Annotate(this._instance, settings._instance, textPtr, boundingAreaPtr, gravity, angle, exception);
});
});
});
});
}

autoGamma(): void;
autoGamma(channelsOrUndefined?: Channels): void {
this.useExceptionPointer(exception => {
Expand Down
61 changes: 61 additions & 0 deletions tests/magick-image/annotate.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
Copyright Dirk Lemstra https://github.com/dlemstra/magick-wasm.
Licensed under the Apache License, Version 2.0.
*/

import { Gravity } from '@src/enums/gravity';
import { MagickGeometry } from '@src/types/magick-geometry';
import { TestFiles } from '@test/test-files';

describe('MagickImage#annotate', () => {
it('should draw the text on the image', () => {
TestFiles.Images.empty150x150Canvas.use(image => {
image.settings.fontPointsize = 70;
image.settings.font = TestFiles.Fonts.kaushanScriptRegularTtf.name;
image.annotate('tESt', Gravity.East);

expect(image).toHavePixelWithColor(10, 70, '#000000ff');
expect(image).toHavePixelWithColor(45, 70, '#000000ff');
expect(image).toHavePixelWithColor(90, 70, '#000000ff');
expect(image).toHavePixelWithColor(145, 70, '#000000ff');
});
});

it('should use the specified bounding box', () => {
TestFiles.Images.empty150x150Canvas.use(image => {
image.settings.fontPointsize = 50;
image.settings.font = TestFiles.Fonts.kaushanScriptRegularTtf.name;
image.annotate('tESt', new MagickGeometry(0, 0, 100, 100), Gravity.East);

expect(image).toHavePixelWithColor(0, 47, '#000000ff');
expect(image).toHavePixelWithColor(24, 47, '#000000ff');
expect(image).toHavePixelWithColor(55, 47, '#000000ff');
expect(image).toHavePixelWithColor(96, 47, '#000000ff');
});
});

it('should use the specified angle', () => {
TestFiles.Images.empty150x150Canvas.use(image => {
image.settings.fontPointsize = 70;
image.settings.font = TestFiles.Fonts.kaushanScriptRegularTtf.name;
image.annotate('tESt', Gravity.Center, 25);

expect(image).toHavePixelWithColor(26, 100, '#000000ff');
expect(image).toHavePixelWithColor(64, 76, '#000000ff');
expect(image).toHavePixelWithColor(121, 74, '#000000ff');
expect(image).toHavePixelWithColor(139, 74, '#000000ff');
});
});

it('should use the specified angle and bounding box', () => {
TestFiles.Images.empty150x150Canvas.use(image => {
image.settings.fontPointsize = 50;
image.settings.font = TestFiles.Fonts.kaushanScriptRegularTtf.name;
image.annotate('tESt', new MagickGeometry(0, 0, 50, 50), Gravity.Center, 100);

expect(image).toHavePixelWithColor(39, 0, '#000000ff');
expect(image).toHavePixelWithColor(27, 32, '#000000ff');
expect(image).toHavePixelWithColor(44, 65, '#000000ff');
});
});
});

0 comments on commit ee5a20b

Please sign in to comment.