diff --git a/src/blend2d/bindings/bl_context.nim b/src/blend2d/bindings/bl_context.nim index 51480fb..ec5e5cd 100644 --- a/src/blend2d/bindings/bl_context.nim +++ b/src/blend2d/bindings/bl_context.nim @@ -91,22 +91,45 @@ type BL_CLIP_MODE_COUNT ## Count of clip modes. BLCompOp* {.size:sizeof(uint32).} = enum - BL_COMP_OP_SRC_OVER ## Source-over [default]. + BL_COMP_OP_SRC_OVER + ## Source-over [default] + ## The source is composited over the destination. this is the default + ## alpha blending compose method, when neither the compose setting is + ## set, nor is set in the image meta-data. BL_COMP_OP_SRC_COPY ## Source-copy. - BL_COMP_OP_SRC_IN ## Source-in. + BL_COMP_OP_SRC_IN + ## Source-in. + ## The destination is composited over the source and the + ## result replaces the destination. BL_COMP_OP_SRC_OUT ## Source-out. - BL_COMP_OP_SRC_ATOP ## Source-atop. + BL_COMP_OP_SRC_ATOP + ## Source-atop. + ## The part of the source lying inside of the destination + ## is composited onto the destination BL_COMP_OP_DST_OVER ## Destination-over. BL_COMP_OP_DST_COPY ## Destination-copy [nop]. BL_COMP_OP_DST_IN ## Destination-in. BL_COMP_OP_DST_OUT ## Destination-out. - BL_COMP_OP_DST_ATOP ## Destination-atop. - BL_COMP_OP_XOR ## Xor. + BL_COMP_OP_DST_ATOP + ## Destination-atop. + ## The part of the destination lying inside of the source is + ## composited over the source and replaces the destination. + ## Areas not overlaid are cleared + BL_COMP_OP_XOR + ## The part of the source that lies outside of the destination + ## is combined with the part of the destination that lies outside + ## of the source. Source or Destination, but not both BL_COMP_OP_CLEAR ## Clear. BL_COMP_OP_PLUS ## Plus. BL_COMP_OP_MINUS ## Minus. BL_COMP_OP_MODULATE ## Modulate. - BL_COMP_OP_MULTIPLY ## Multiply. + BL_COMP_OP_MULTIPLY + ## Multiply. + ## The source is multiplied by the destination and replaces the + ## destination. The resultant color is always at least as dark + ## as either of the two constituent colors. Multiplying any + ## color with black produces black. Multiplying any color + ## with white leaves the original color unchanged. BL_COMP_OP_SCREEN ## Screen. BL_COMP_OP_OVERLAY ## Overlay. BL_COMP_OP_DARKEN ## Darken. diff --git a/src/blend2d/bindings/bl_filesystem.nim b/src/blend2d/bindings/bl_filesystem.nim index ee7e5f3..efff7ce 100644 --- a/src/blend2d/bindings/bl_filesystem.nim +++ b/src/blend2d/bindings/bl_filesystem.nim @@ -1,4 +1,33 @@ +# Copyright (c) 2017-2024 The Blend2D Authors +# +# This software is provided 'as-is', without any express or +# implied warranty. In no event will the authors be held liable +# for any damages arising from the use of this software. +# +# Permission is granted to anyone to use this software for any purpose, +# including commercial applications, and to alter it and redistribute it +# freely, subject to the following restrictions: +# +# The origin of this software must not be misrepresented; you must not +# claim that you wrote the original software. If you use this software in +# a product, an acknowledgment in the product documentation would be +# appreciated but is not required. +# +# Altered source versions must be plainly marked as such, and must not +# be misrepresented as being the original software. +# +# This notice may not be removed or altered from any source distribution. +# +# +# blend2d-nim is a Nim wrapper for the libblend2d library +# providing low-level & high-level API for rendering 2D graphics. +# +# (c) 2024 George Lemon | ZLib License +# Made by Humans from OpenPeeps +# https://github.com/openpeeps/blend2d-nim + import ./bl_globals + {.push importc, header: blend2dHeader.} type diff --git a/src/blend2d/bindings/bl_geometry.nim b/src/blend2d/bindings/bl_geometry.nim index 1268346..3c5cadd 100644 --- a/src/blend2d/bindings/bl_geometry.nim +++ b/src/blend2d/bindings/bl_geometry.nim @@ -165,8 +165,7 @@ type BLSizeI* {.bycopy.} = object ## Size specified as [w, h] using int as a storage type. - w*: cint - h*: cint + w*, h*: cint BLBox* {.bycopy.} = object ## Box specified as [x0, y0, x1, y1] using double as a storage type. diff --git a/src/blend2d/bindings/bl_text.nim b/src/blend2d/bindings/bl_text.nim index 809dd57..7f64752 100644 --- a/src/blend2d/bindings/bl_text.nim +++ b/src/blend2d/bindings/bl_text.nim @@ -592,10 +592,19 @@ proc blFontApplyKerning*(self: ptr BLFontCore; gb: ptr BLGlyphBufferCore): BLRes proc blFontApplyGSub*(self: ptr BLFontCore; gb: ptr BLGlyphBufferCore; lookups: ptr BLBitArrayCore): BLResult proc blFontApplyGPos*(self: ptr BLFontCore; gb: ptr BLGlyphBufferCore; lookups: ptr BLBitArrayCore): BLResult proc blFontGetTextMetrics*(self: ptr BLFontCore; gb: ptr BLGlyphBufferCore; `out`: ptr BLTextMetrics): BLResult -proc blFontGetGlyphBounds*(self: ptr BLFontCore; glyphData: ptr uint32; glyphAdvance: ptr int; `out`: ptr BLBoxI; count: uint): BLResult -proc blFontGetGlyphAdvances*(self: ptr BLFontCore; glyphData: ptr uint32; glyphAdvance: ptr int; `out`: ptr BLGlyphPlacement; count: uint): BLResult -proc blFontGetGlyphOutlines*(self: ptr BLFontCore; glyphId: BLGlyphId; userTransform: ptr BLMatrix2D; `out`: ptr BLPathCore; sink: BLPathSinkFunc; userData: pointer): BLResult -proc blFontGetGlyphRunOutlines*(self: ptr BLFontCore; glyphRun: ptr BLGlyphRun; userTransform: ptr BLMatrix2D; `out`: ptr BLPathCore; sink: BLPathSinkFunc; userData: pointer): BLResult +proc blFontGetGlyphBounds*(self: ptr BLFontCore; glyphData: ptr uint32; + glyphAdvance: ptr int; `out`: ptr BLBoxI; count: uint): BLResult + +proc blFontGetGlyphAdvances*(self: ptr BLFontCore; glyphData: ptr uint32; + glyphAdvance: ptr int; `out`: ptr BLGlyphPlacement; count: uint): BLResult + +proc blFontGetGlyphOutlines*(self: ptr BLFontCore; glyphId: BLGlyphId; + userTransform: ptr BLMatrix2D; `out`: ptr BLPathCore; + sink: BLPathSinkFunc; userData: pointer): BLResult + +proc blFontGetGlyphRunOutlines*(self: ptr BLFontCore; glyphRun: ptr BLGlyphRun; + userTransform: ptr BLMatrix2D; `out`: ptr BLPathCore; + sink: BLPathSinkFunc; userData: pointer): BLResult # # BLFontFeatureSettings API diff --git a/src/blend2d/color.nim b/src/blend2d/color.nim index f27c2f1..1ba8cac 100644 --- a/src/blend2d/color.nim +++ b/src/blend2d/color.nim @@ -27,6 +27,8 @@ # https://github.com/openpeeps/blend2d-nim import ./bindings/[bl_globals, bl_style] +from ./bindings import `!` + proc rgba*(r, g, b, a: float32): BLRgba = result = BLRgba(r: r, g: g, b: b, a: a) @@ -333,22 +335,32 @@ const colYellow* = ColorHex(0xFFFFFF00) colYellowGreen* = ColorHex(0xFF9ACD32) +type + Gradient* = ptr BLGradientCore + # # Gradients # -proc newLinearGradient*(x1, y1: cdouble; x0, y0: cdouble = 0): ptr BLGradientCore = - ## Create a new `BLGradientCore` - var gradient = create(BLGradientCore) - var values = BLLinearGradientValues(x0: x0, y0: y0, x1: x1, y1: y1) - assert blGradientInitAs(gradient, - BLGradientType.BL_GRADIENT_TYPE_LINEAR, values.addr, - BLExtendMode.BL_EXTEND_MODE_PAD, nil, 0, nil).code == BL_SUCCESS - gradient +proc linearGradient*(x1, y1: cdouble; x0, y0: cdouble = 0): Gradient = + ## Create a new linear `Gradient` pointer of `BLGradientCore` + result = create(BLGradientCore) + var v = BLLinearGradientValues(x0: x0, y0: y0, x1: x1, y1: y1) + !blGradientInitAs(result, + BLGradientType.BL_GRADIENT_TYPE_LINEAR, v.addr, + BLExtendMode.BL_EXTEND_MODE_PAD, nil, 0, nil) + +proc radialGradient*(x1, y1, x0, y0, r0: float): Gradient = + ## Create a new radial `Gradient` pointer of `BLGradientCore` + result = create(BLGradientCore) + var v = BLRadialGradientValues(x0: x0, y0: y0, x1: x1, y1: y1, r0: r0) + !blGradientInitAs(result, + BLGradientType.BL_GRADIENT_TYPE_RADIAL, v.addr, + BLExtendMode.BL_EXTEND_MODE_PAD, nil, 0, nil) -proc add*(gradient: ptr BLGradientCore, offset: float32, color: uint32) = - ## Add a new stop color to `gradient` using `offset` and `color` - assert gradient.blGradientAddStopRgba32(offset, color).code == BL_SUCCESS +proc add*(gr: Gradient, offset: float32, color: uint32) = + ## Add a new stop color to `Gradient` using `offset` and `color` + !blGradientAddStopRgba32(gr, offset, color) -proc add*(gradient: ptr BLGradientCore, stop: BLGradientStop) = - ## Add a new `BLGradientStop` stop color to `gradient` - assert gradient.blGradientAssignStops(stop.addr, 0'u).code == BL_SUCCESS +proc add*(gr: Gradient, stop: BLGradientStop) = + ## Add a new `BLGradientStop` stop color to `Gradient` + !blGradientAssignStops(gr, stop.addr, 0'u) diff --git a/src/blend2d/context.nim b/src/blend2d/context.nim index 9469b00..0c8b8d1 100644 --- a/src/blend2d/context.nim +++ b/src/blend2d/context.nim @@ -36,13 +36,19 @@ export BLPoint, BLPointI, context type Context* = ptr BLContextCore -proc newContext*(img: Image, compOp: BLCompOp = BL_COMP_OP_SRC_COPY): Context = +proc setCompOp*(ctx: Context, compOp: BLCompOp) {.inline.} = + !blContextSetCompOp(ctx, compOp) + +proc clearAll*(ctx: Context) {.inline.} = + !blContextClearAll(ctx) + +proc newContext*(img: Image, compOp: BLCompOp = BL_COMP_OP_SRC_OVER): Context = ## Creates a new Context result = create(BLContextCore) var cci = create(BLContextCreateInfo) !blContextInitAs(result, img, cci) - !blContextClearAll(result) - !blContextSetCompOp(result, compOp) + result.clearAll() + result.setCompOp(compOp) proc opacity*(ctx: Context, alpha: range[0.0..1.0]): Context {.discardable.} = ## Applies `blContextSetGlobalAlpha` using `alpha` @@ -56,10 +62,10 @@ template opacity*(alpha: range[0.0..1.0], stmts: untyped) {.dirty.} = stmts this.opacity(1) -proc fillStyle*(ctx: Context): Context {.discardable.} = - ## Fill `ctx` Context with Color +proc fillStyle*(ctx: Context, color: ColorHex = colWhite): Context {.discardable.} = + ## Fill Context with ColorHex !blContextSetCompOp(ctx, BLCompOp.BL_COMP_OP_SRC_COPY) - !blContextSetFillStyleRgba32(ctx, 0xFFFFFFFF'u32) + !blContextSetFillStyleRgba32(ctx, color) !blContextFillAll(ctx) ctx @@ -68,21 +74,11 @@ proc fill*(ctx: Context, rgba: BLRgba): Context {.discardable.} = !blContextFillAllExt(ctx, rgba.addr) ctx -# proc fillAllC*(impl: ptr BLContextImpl): BLResult {.cdecl.} = - # blContextFillAllExt(impl[], gradient) - proc fill*(ctx: Context, gradient: ptr BLGradientCore): Context {.discardable.} = ## Fills the entire Context with the `gradient` color !blContextFillAllExt(ctx, gradient) ctx -proc add*(ctx: Context, font: Font, content: string, x, y: int32 = 0, - color: ColorHex = colWhite): Context {.discardable.} = - ## Add a new text to Context - let origin = point(x, y) - !blContextFillUtf8TextIRgba32(ctx, origin.addr, font, - content.cstring, content.len.uint, color) - # # Context Transformers # @@ -107,7 +103,7 @@ proc resetTransform*(ctx: Context) = !ctx.blContextApplyTransformOp(BlTransformOpReset, nil) # -# Blendings +# Blending Modes # proc blend*(ctx: Context, img: Image, p: ptr BLPointI, r: ptr BLRectI, @@ -124,23 +120,24 @@ macro genBlendingModes(blendModes: static seq[string]) = let id = ident("blend" & blendMode) let mode = ident("BLCompOp" & blendMode) add result, quote do: - # Generate blending mode handles that calls `blContextSetCompOp`. - # A `blendClear` call is required to stop blending handle. + # Generate blend mode handles that calls `blContextSetCompOp`. + # A `blendSrcOver` call is required after this in order + # to clear the previously applied blending. proc `id`*(ctx: Context): Context {.discardable.} = !ctx.blContextSetCompOp(`mode`) ctx let handleId = "blend" & blendMode let macroHandleID = ident("apply" & id.strVal.capitalizeAscii) add result, quote do: - # Generate macro-based blending mode handles that injects - # a `blendClear` call at the end of the statement. - # These macros can be used only inside a `ctx` macro + # Generate macro-based blend mode handles that auto-injects + # a `blendSrcOver` call at the end of the statement to switch + # to default composition mode. macro `macroHandleID`*(stmts: untyped): untyped = result = newStmtList() result.add( newCall(ident(`handleId`), ident"this"), stmts, - newCall(ident"blendClear", ident"this") + newCall(ident"blendSrcOver", ident"this") ) genBlendingModes @["Clear", "SrcOver", "SrcCopy", "SrcIn", "SrcOut", "SrcAtop", @@ -149,17 +146,109 @@ genBlendingModes @["Clear", "SrcOver", "SrcCopy", "SrcIn", "SrcOut", "SrcAtop", "Darken", "Lighten", "ColorDodge", "ColorBurn", "LinearBurn", "LinearLight", "PinLight", "HardLight", "SoftLight", "Difference", "Exclusion"] -proc add*(ctx: Context, img: Image, p: ptr BLPointI, r: ptr BLRectI): Context {.discardable.} = - !ctx.blContextBlitImageI(p, img, r) +# +# Context - Add font +# +proc add*(ctx: Context, font: Font, content: string, x, y: int32 = 0, + color: ColorHex = colWhite): Context {.discardable.} = + ## Add a new text to Context + let origin = point(x, y) + !blContextFillUtf8TextIRgba32(ctx, origin.addr, font, + content.cstring, content.len.uint, color) + +# +# Context - Add Geometry +# +proc add*(ctx: Context, r: BLRectI) = + ## Add a `BLRectI` to Context + !blContextFillRectI(ctx, r.addr) + +proc add*(ctx: Context, circle: Circle) = + ## Add a `Circle` pointer to current `Context` + !blContextFillGeometry(ctx, BL_GEOMETRY_TYPE_CIRCLE, circle) + +proc add*(ctx: Context, c: Circle, color: ColorHex) = + ## Add a colored `Circle` pointer to current `Context` + !blContextFillGeometryRgba32(ctx, BL_GEOMETRY_TYPE_CIRCLE, c, color) + +proc add*(ctx: Context, rr: RoundRect, color: ColorHex) = + ## Add a colored `BLRoundRect` to current `Context` + !blContextFillGeometryRgba32(ctx, BL_GEOMETRY_TYPE_ROUND_RECT, rr, color) + +# +# Context - Blit Image with BLPointI +# +proc add*(ctx: Context, img: Image): Context {.discardable.} = + ## Add an Image to current Context + var p = point(0, 0) + var r = rect(img.width, img.height) + !ctx.blContextBlitImageI(p.addr, img, r.addr) + ctx + +proc add*(ctx: Context, img: Image, + p: sink BLPointI, r: sink BLRectI): Context {.discardable.} = + ## Add an Image to current Context using `BLPointI` as + ## position origin and `BLRectI` as image area + !ctx.blContextBlitImageI(p.addr, img, r.addr) + ctx + +proc add*(ctx: Context, img: Image, + p: sink BLPointI): Context {.discardable.} = + ## Add an Image to current Context using `BLPointI` position origin + var r = rect(img.width, img.height) + !ctx.blContextBlitImageI(p.addr, img, r.addr) ctx +# +# Context - Blit Image with BLPoint +# +proc add*(ctx: Context, img: Image, + p: sink BLPoint, r: sink BLRectI): Context {.discardable.} = + ## Add an Image to current Context using `BLPointI` as + ## position origin and `BLRectI` as image area + !ctx.blContextBlitImageD(p.addr, img, r.addr) + ctx + +proc add*(ctx: Context, img: Image, + p: sink BLPoint): Context {.discardable.} = + ## Add an Image to current Context using `BLPointI` position origin + var r = rect(img.width, img.height) + !ctx.blContextBlitImageD(p.addr, img, r.addr) + ctx + +proc add*(ctx: Context, img: Image, r: sink BLRectI): Context {.discardable.} = + ## Add an Image to current Context using `BLRectI` image area + var p = point(0.0, 0.0) + !ctx.blContextBlitImageD(p.addr, img, r.addr) + ctx + +# +# Context - Masks +# +proc mask*(ctx: Context, origin: PointI, + mask: Image, maskArea: RectI): Context {.discardable.} = + !blContextFillMaskI(ctx, origin, mask, maskArea) + ctx + +proc mask*(ctx: Context, text: tuple[f: BLFontCore, textContent: string, x: int32, y: int32], img: BLImageCore) = + !blContextSetCompOp(ctx, BLCompOp.BL_COMP_OP_SRC_COPY) + ctx.add(text[0].addr, text[1], 0, 0) + var p = point(0, 0) + var r = rect(300, 300) + !blContextSetCompOp(ctx, BLCompOp.BL_COMP_OP_SRC_IN) + !ctx.blContextBlitImageI(p.addr, img.addr, r.addr) + proc endContext*(ctx: Context) = !blContextEnd(ctx) # # Macro Utils # -macro ctx*(img: typed, contextStmt: untyped) = +macro ctx*(img: typed, contextStmt: untyped): untyped = + ## Macro utility to create a new nestable context. + ## + ## Use `this` identifier to access the Context pointer. + ## An `endContext` call is injected at the end of the statement. result = nnkBlockStmt.newTree() add result, newEmptyNode() # unamed block add result, diff --git a/src/blend2d/font.nim b/src/blend2d/font.nim index 7cf984c..04a9fc0 100644 --- a/src/blend2d/font.nim +++ b/src/blend2d/font.nim @@ -47,14 +47,21 @@ proc load*(f: FontFace, path: string, readFlags: BLFileReadFlags = BL_FILE_READ_ !blFontFaceCreateFromFile(f, path, readFlags) f -proc newTypeface*(path: string, readFlags: BLFileReadFlags = BL_FILE_READ_NO_FLAGS): FontFace = - ## Initialize a new `Font` reading a font from `path` +proc loadTypeface*(path: string, readFlags: BLFileReadFlags = BL_FILE_READ_NO_FLAGS): FontFace = + ## Initialize a new `Font` by reading a font from `path` newTypeface().load(path, readFlags) -proc font*(f: FontFace, size: float = 16.0): Font = +proc initFont*(f: FontFace, fontSize: float = 16.0): Font = + ## Initialize a `BLFontCore` and returns the `Font` pointer result = create(BLFontCore) !blFontInit(result) - !blFontCreateFromFace(result, f, size) + !blFontCreateFromFace(result, f, fontSize) + +proc initFontCore*(f: FontFace, fontSize: float = 16.0): BLFontCore = + ## Initialize a `BLFontCore` and returns the `Font` pointer + result = BLFontCore() + !blFontInit(result.addr) + !blFontCreateFromFace(result.addr, f, fontSize) proc size*(f: Font): float = ## Returns the current font size @@ -71,3 +78,25 @@ proc metrics*(f: Font, data: ptr string): ptr BLTextMetrics = !blGlyphBufferSetText(gb, data, len(data[]).uint, BL_TEXT_ENCODING_UTF8) !blFontShape(f, gb) !blFontGetTextMetrics(f, gb, result) + +# +# Font Manager +# +type + FontManager* = ptr BLFontManagerCore + +proc initFontManager*(): FontManager = + result = create(BLFontManagerCore) + !blFontManagerInit(result) + +proc loadTypeface*(fm: FontManager, path: string) = + var tf = path.loadTypeface() + !blFontManagerAddFace(fm, tf) + +proc getTypeface*(fm: FontManager, name: string): FontFace = + result = create(BLFontFaceCore) + !blFontFaceInit(result) + !blFontManagerQueryFace(fm, name.cstring, len(name).uint, nil, result) + +proc destroyFontManager*(fm: FontManager) = + !blFontManagerDestroy(fm) \ No newline at end of file diff --git a/src/blend2d/geometry.nim b/src/blend2d/geometry.nim index f60c6bb..398608f 100644 --- a/src/blend2d/geometry.nim +++ b/src/blend2d/geometry.nim @@ -26,11 +26,17 @@ # Made by Humans from OpenPeeps # https://github.com/openpeeps/blend2d-nim import ./bindings/bl_geometry +type + RectI* = ptr BLRectI + PointI* = ptr BLPointI + Point* = ptr BLPoint + Circle* = ptr BLCircle + RoundRect* = ptr BLRoundRect -proc point*(x, y: cdouble): BLPoint = +proc point*(x, y: float): BLPoint = BLPoint(x: x, y: y) -proc point*(x, y: int32): BLPointI = +proc point*(x, y: int32 = 0): BLPointI = BLPointI(x: x, y: y) proc rect*(x, y, w, h: int32): BLRectI = @@ -38,3 +44,30 @@ proc rect*(x, y, w, h: int32): BLRectI = proc rect*(w, h: int32): BLRectI = rect(0, 0, w, h) + +proc square*(size: int32, x, y: int32 = 0): BLRectI = + rect(x, y, size, size) + +proc rect*(x, y, w, h, rx, ry: float): BLRoundRect = + BLRoundRect(x: x, y: y, w: w, h: h, rx: rx, ry: ry) + +proc roundRect*(x, y, w, h: float; rx, ry = 10.0): BLRoundRect {.inline.} = + rect(x, y, w, h, rx, ry) + +proc roundSquare*(x, y, size: float, rx, ry = 10.0): BLRoundRect {.inline.} = + rect(x, y, size, size, rx, ry) + +proc roundSquare*(x, y, size, radius: float): BLRoundRect {.inline.} = + rect(x, y, size, size, radius, radius) + +proc size*(w, h: float): BLSize = + BLSize(w: w, h: h) + +proc size*(w, h: int32): BLSizeI = + BLSizeI(w: w, h: h) + +proc size*(w, h: int): BLSizeI = + BLSizeI(w: w.int32, h: h.int32) + +proc circle*(cx, cy, r: float): BLCircle = + result = BLCircle(cx: cx, cy: cy, r: r) diff --git a/src/blend2d/image.nim b/src/blend2d/image.nim index 5f14655..cff1cb1 100644 --- a/src/blend2d/image.nim +++ b/src/blend2d/image.nim @@ -25,7 +25,7 @@ # (c) 2024 George Lemon | ZLib License # Made by Humans from OpenPeeps # https://github.com/openpeeps/blend2d-nim -import std/[os, strutils] +import std/[macros, os, strutils] import ./bindings/[bl_globals, bl_image] import ./geometry @@ -44,7 +44,6 @@ proc init*(width, height: int, pixelFormat: BLFormat = BL_FORMAT_PRGB32): Image proc open*(path: string): Image = result = create(BLImageCore) !blImageInit(result) - # assert blImageCodecInitByName(result.codecCore, ext, len(ext).uint, nil).code == BLSuccess !blImageReadFromFile(result, path.cstring, nil) proc getAspectRatio*(width, height, minWidth, minHeight: int): (int, int) = @@ -56,7 +55,8 @@ proc getData*(img: Image): ImageData = result = create(BLImageData) !blImageGetData(img, result) -proc resize*(img: Image, width, height: int32, scaleFilter: BLImageScaleFilter = BLImageScaleFilterNearest): Image {.discardable.} = +proc resize*(img: Image, width, height: int32, + scaleFilter: BLImageScaleFilter = BLImageScaleFilterNearest): Image {.discardable.} = ## Resize an image by maintaining the aspect ratio and returns a new Image let data = img.getData let (w, h) = getAspectRatio(data[].size.w, data[].size.h, width, height) @@ -65,7 +65,8 @@ proc resize*(img: Image, width, height: int32, scaleFilter: BLImageScaleFilter = !blImageInit(result) !blImageScale(result, img, s.addr, scaleFilter) -proc resize*(img: var Image, width, height: int32, scaleFilter: BLImageScaleFilter = BLImageScaleFilterNearest) = +proc resize*(img: var Image, width, height: int32, + scaleFilter: BLImageScaleFilter = BLImageScaleFilterNearest) = ## Resize a mutable image, keeping the aspect ratio var data = img.getData let (w, h) = getAspectRatio(data[].size.w, data[].size.h, width, height) @@ -73,9 +74,25 @@ proc resize*(img: var Image, width, height: int32, scaleFilter: BLImageScaleFilt !blImageMakeMutable(img, data) !blImageScale(img, img, s.addr, scaleFilter) +proc getWidth*(img: Image): int32 = + ## Retrieve the width of an Image + result = img.getData[].size.w + +proc getHeight*(img: Image): int32 = + ## Retrieve the height of an Image + result = img.getData[].size.h + +proc width*(img: Image): int32 {.inline.} = + ## An alias of `getWidth` + img.getWidth() + +proc height*(img: Image): int32 {.inline.} = + ## An alias of `getHeight` + img.getHeight() + proc exportAs*(img: Image, path: string) = ## Exports an Image to given `path`. - ## The codec is automatically + ## The codec is initialized from path extension let ext = path.splitFile.ext.replace(".").toUpperAscii var codec = create(BLImageCodecCore) !blImageCodecInitByName(codec, ext, len(ext).uint, nil) @@ -83,4 +100,30 @@ proc exportAs*(img: Image, path: string) = proc destroyImage*(img: Image) = !blImageDestroy(img) - dealloc(img) \ No newline at end of file + dealloc(img) + +# +# Macro utils +# +macro group*(imgIdent: untyped, width, height, contextStmt: untyped): untyped = + ## A macro utility that help organize your + ## 2D computations in grouped-like layers. + ## + ## This macro calls the `ctx` macro utility, which + ## expose the current Context as `this` identifier. + result = newStmtList() + add result, + nnkVarSection.newTree( + nnkIdentDefs.newTree( + imgIdent, + ident"Image", + newCall( + newDotExpr( + ident"image", + ident"init", + ), + width, height + ) + ) + ), + newCall(ident"ctx", imgIdent, contextStmt)