diff --git a/src/pixie.nim b/src/pixie.nim index 51900bd..d797132 100644 --- a/src/pixie.nim +++ b/src/pixie.nim @@ -69,10 +69,6 @@ proc encodeMask*(mask: Mask, fileFormat: FileFormat): string = else: raise newException(PixieError, "Unsupported file format") -proc writeFile*(image: Image, filePath: string, fileFormat: FileFormat) = - ## Writes an image to a file. - writeFile(filePath, image.encodeImage(fileFormat)) - proc writeFile*(image: Image, filePath: string) = ## Writes an image to a file. let fileFormat = case splitFile(filePath).ext.toLowerAscii(): @@ -81,11 +77,7 @@ proc writeFile*(image: Image, filePath: string) = of ".jpg", ".jpeg": ffJpg else: raise newException(PixieError, "Unsupported file extension") - image.writeFile(filePath, fileformat) - -proc writeFile*(mask: Mask, filePath: string, fileFormat: FileFormat) = - ## Writes an mask to a file. - writeFile(filePath, mask.encodeMask(fileFormat)) + writeFile(filePath, image.encodeImage(fileFormat)) proc writeFile*(mask: Mask, filePath: string) = ## Writes a mask to a file. @@ -95,7 +87,7 @@ proc writeFile*(mask: Mask, filePath: string) = of ".jpg", ".jpeg": ffJpg else: raise newException(PixieError, "Unsupported file extension") - mask.writeFile(filePath, fileformat) + writeFile(filePath, mask.encodeMask(fileFormat)) proc fillRect*( mask: Mask, diff --git a/src/pixie/fileformats/png.nim b/src/pixie/fileformats/png.nim index 15f488c..c99d10a 100644 --- a/src/pixie/fileformats/png.nim +++ b/src/pixie/fileformats/png.nim @@ -421,10 +421,8 @@ proc decodePng*(data: string): Image = var pixels = decodeImageData(header, palette, transparency, imageData) pixels.toPremultipliedAlpha() - result = Image() - result.width = header.width - result.height = header.height - result.data = cast[seq[ColorRGBX]](pixels) + result = newImage(header.width, header.height) + copyMem(result.data[0].addr, pixels[0].addr, pixels.len * 4) proc encodePng*(width, height, channels: int, data: pointer, len: int): string = ## Encodes the image data into the PNG file format. diff --git a/src/pixie/images.nim b/src/pixie/images.nim index 7c5fd88..dede0bf 100644 --- a/src/pixie/images.nim +++ b/src/pixie/images.nim @@ -71,6 +71,10 @@ proc `[]`*(image: Image, x, y: int): ColorRGBX {.inline.} = if image.inside(x, y): return image.getRgbaUnsafe(x, y) +proc getColor*(image: Image, x, y: int): Color = + ## Gets a color at (x, y) or returns transparent black if outside of bounds. + image[x, y].color() + proc setRgbaUnsafe*(image: Image, x, y: int, color: SomeColor) {.inline.} = ## Sets a color from (x, y) coordinates. ## * No bounds checking * @@ -83,6 +87,10 @@ proc `[]=`*(image: Image, x, y: int, color: SomeColor) {.inline.} = if image.inside(x, y): image.setRgbaUnsafe(x, y, color.asRgbx()) +proc setColor*(image: Image, x, y: int, color: Color) = + ## Sets a color at (x, y) or does nothing if outside of bounds. + image[x, y] = color.rgbx() + proc fillUnsafe*(data: var seq[ColorRGBX], color: SomeColor, start, len: int) = ## Fills the image data with the parameter color starting at index start and ## continuing for len indices. @@ -807,7 +815,7 @@ proc drawUber(a, b: Image | Mask, mat = mat3(), blendMode = bmNormal) = zeroMem(a.data[a.dataIndex(xMax, y)].addr, 4 * (a.width - xMax)) proc draw*( - a, b: Image, transform: Vec2 | Mat3 = vec2(), blendMode = bmNormal + a, b: Image, transform: Mat3 = mat3(), blendMode = bmNormal ) {.inline.} = ## Draws one image onto another using matrix with color blending. when type(transform) is Vec2: @@ -816,7 +824,7 @@ proc draw*( a.drawUber(b, transform, blendMode) proc draw*( - a, b: Mask, transform: Vec2 | Mat3 = vec2(), blendMode = bmMask + a, b: Mask, transform: Mat3 = mat3(), blendMode = bmMask ) {.inline.} = ## Draws a mask onto a mask using a matrix with color blending. when type(transform) is Vec2: @@ -825,7 +833,7 @@ proc draw*( a.drawUber(b, transform, blendMode) proc draw*( - image: Image, mask: Mask, transform: Vec2 | Mat3 = vec2(), blendMode = bmMask + image: Image, mask: Mask, transform: Mat3 = mat3(), blendMode = bmMask ) {.inline.} = ## Draws a mask onto an image using a matrix with color blending. when type(transform) is Vec2: @@ -834,7 +842,7 @@ proc draw*( image.drawUber(mask, transform, blendMode) proc draw*( - mask: Mask, image: Image, transform: Vec2 | Mat3 = vec2(), blendMode = bmMask + mask: Mask, image: Image, transform: Mat3 = mat3(), blendMode = bmMask ) {.inline.} = ## Draws a image onto a mask using a matrix with color blending. when type(transform) is Vec2: @@ -860,25 +868,14 @@ proc resize*(srcImage: Image, width, height: int): Image = bmOverwrite ) -proc shift*(target: Image | Mask, offset: Vec2) = - ## Shifts the target by offset. - if offset != vec2(0, 0): - let copy = target.copy() # Copy to read from - - # Reset target for being drawn to - when type(target) is Image: - target.fill(rgbx(0, 0, 0, 0)) - else: - target.fill(0) - - target.draw(copy, offset, bmOverwrite) # Draw copy at offset - proc shadow*( image: Image, offset: Vec2, spread, blur: float32, color: SomeColor ): Image = ## Create a shadow of the image with the offset, spread and blur. - let mask = image.newMask() - mask.shift(offset) + let + mask = image.newMask() + shifted = newMask(mask.width, mask.height) + shifted.draw(mask, translate(offset), bmOverwrite) mask.spread(spread) mask.blur(blur) result = newImage(mask.width, mask.height) diff --git a/src/pixie/masks.nim b/src/pixie/masks.nim index fdcaa01..b234032 100644 --- a/src/pixie/masks.nim +++ b/src/pixie/masks.nim @@ -50,10 +50,14 @@ proc getValueUnsafe*(mask: Mask, x, y: int): uint8 {.inline.} = result = mask.data[mask.width * y + x] proc `[]`*(mask: Mask, x, y: int): uint8 {.inline.} = - ## Gets a pixel at (x, y) or returns transparent black if outside of bounds. + ## Gets a value at (x, y) or returns transparent black if outside of bounds. if mask.inside(x, y): return mask.getValueUnsafe(x, y) +proc getValue*(mask: Mask, x, y: int): uint8 {.inline.} = + ## Gets a value at (x, y) or returns transparent black if outside of bounds. + mask[x, y] + proc setValueUnsafe*(mask: Mask, x, y: int, value: uint8) {.inline.} = ## Sets a value from (x, y) coordinates. ## * No bounds checking * @@ -66,6 +70,10 @@ proc `[]=`*(mask: Mask, x, y: int, value: uint8) {.inline.} = if mask.inside(x, y): mask.setValueUnsafe(x, y, value) +proc setValue*(mask: Mask, x, y: int, value: uint8) {.inline.} = + ## Sets a value at (x, y) or does nothing if outside of bounds. + mask[x, y] = value + proc minifyBy2*(mask: Mask, power = 1): Mask = ## Scales the mask down by an integer scale. if power < 0: diff --git a/src/pixie/paints.nim b/src/pixie/paints.nim index a6ee40f..5d02a48 100644 --- a/src/pixie/paints.nim +++ b/src/pixie/paints.nim @@ -25,8 +25,8 @@ type ColorStop* = object ## Color stop on a gradient curve. - color*: ColorRGBX ## Color of the stop - position*: float32 ## Gradient Stop position 0..1. + color*: ColorRGBX ## Color of the stop. + position*: float32 ## Gradient stop position 0..1. SomePaint* = string | Paint | SomeColor @@ -96,12 +96,9 @@ proc gradientPut( color = color.applyOpacity(paint.opacity) image.setRgbaUnsafe(x, y, color.rgba.rgbx()) -proc fillGradientLinear*(image: Image, paint: Paint) = +proc fillGradientLinear(image: Image, paint: Paint) = ## Fills a linear gradient. - if paint.kind != pkGradientLinear: - raise newException(PixieError, "Paint kind must be " & $pkGradientLinear) - if paint.gradientHandlePositions.len != 2: raise newException(PixieError, "Linear gradient requires 2 handles") @@ -121,12 +118,9 @@ proc fillGradientLinear*(image: Image, paint: Paint) = t = toLineSpace(at, to, xy) image.gradientPut(paint, x, y, t, paint.gradientStops) -proc fillGradientRadial*(image: Image, paint: Paint) = +proc fillGradientRadial(image: Image, paint: Paint) = ## Fills a radial gradient. - if paint.kind != pkGradientRadial: - raise newException(PixieError, "Paint kind must be " & $pkGradientRadial) - if paint.gradientHandlePositions.len != 3: raise newException(PixieError, "Radial gradient requires 3 handles") @@ -155,12 +149,9 @@ proc fillGradientRadial*(image: Image, paint: Paint) = t = (mat * xy).length() image.gradientPut(paint, x, y, t, paint.gradientStops) -proc fillGradientAngular*(image: Image, paint: Paint) = +proc fillGradientAngular(image: Image, paint: Paint) = ## Fills an angular gradient. - if paint.kind != pkGradientAngular: - raise newException(PixieError, "Paint kind must be " & $pkGradientAngular) - if paint.gradientHandlePositions.len != 3: raise newException(PixieError, "Angular gradient requires 2 handles") @@ -182,3 +173,16 @@ proc fillGradientAngular*(image: Image, paint: Paint) = angle = normalize(xy - center).angle() t = (angle + gradientAngle + PI / 2).fixAngle() / 2 / PI + 0.5 image.gradientPut(paint, x, y, t, paint.gradientStops) + +proc fillGradient*(image: Image, paint: Paint) = + ## Fills with the Paint gradient. + + case paint.kind: + of pkGradientLinear: + image.fillGradientLinear(paint) + of pkGradientRadial: + image.fillGradientRadial(paint) + of pkGradientAngular: + image.fillGradientAngular(paint) + else: + raise newException(PixieError, "Paint must be a gradient") diff --git a/src/pixie/paths.nim b/src/pixie/paths.nim index 53eb836..8c58ea2 100644 --- a/src/pixie/paths.nim +++ b/src/pixie/paths.nim @@ -1813,12 +1813,8 @@ proc fillPath*( fill.draw(paint.image, paint.imageMat) of pkImageTiled: fill.drawTiled(paint.image, paint.imageMat) - of pkGradientLinear: - fill.fillGradientLinear(paint) - of pkGradientRadial: - fill.fillGradientRadial(paint) - of pkGradientAngular: - fill.fillGradientAngular(paint) + of pkGradientLinear, pkGradientRadial, pkGradientAngular: + fill.fillGradient(paint) paint.opacity = savedOpacity @@ -1909,12 +1905,8 @@ proc strokePath*( fill.draw(paint.image, paint.imageMat) of pkImageTiled: fill.drawTiled(paint.image, paint.imageMat) - of pkGradientLinear: - fill.fillGradientLinear(paint) - of pkGradientRadial: - fill.fillGradientRadial(paint) - of pkGradientAngular: - fill.fillGradientAngular(paint) + of pkGradientLinear, pkGradientRadial, pkGradientAngular: + fill.fillGradient(paint) paint.opacity = savedOpacity diff --git a/tests/images/masks/shifted.png b/tests/images/masks/shifted.png deleted file mode 100644 index 2beb2a4..0000000 Binary files a/tests/images/masks/shifted.png and /dev/null differ diff --git a/tests/test_images.nim b/tests/test_images.nim index a0d0f3c..13b3da0 100644 --- a/tests/test_images.nim +++ b/tests/test_images.nim @@ -43,7 +43,7 @@ block: a.fill(rgba(255, 0, 0, 255)) b.fill(rgba(0, 255, 0, 255)) - a.draw(b, vec2(0, 0)) + a.draw(b, translate(vec2(0, 0))) a.writeFile("tests/images/flipped1.png") a.flipVertical() diff --git a/tests/test_masks.nim b/tests/test_masks.nim index d352902..ef72e94 100644 --- a/tests/test_masks.nim +++ b/tests/test_masks.nim @@ -75,12 +75,6 @@ block: a.draw(b) writeFile("tests/images/masks/imageMaskedMask.png", a.encodePng()) -block: - let a = newMask(100, 100) - a.fill(255) - a.shift(vec2(10, 10)) - writeFile("tests/images/masks/shifted.png", a.encodePng()) - block: let path = newPath() path.rect(40, 40, 20, 20)