///|
/// This file is based on the Go implementation found here:
/// https://cs.opensource.google/go/go/+/refs/tags/go1.23.3:src/image/image.go
/// which has the copyright notice:
/// Copyright 2009 The Go Authors. All rights reserved.
/// Use of this source code is governed by a BSD-style
/// license that can be found in the LICENSE file.
///
/// Package image implements a basic 2-D image library.
///
/// The fundamental trait is called [Image]. An [Image] contains colors, which
/// are described in the image/color package.
///
/// # Security Considerations
///
/// The image package can be used to parse arbitrarily large images, which can
/// cause resource exhaustion on machines which do not have enough memory to
/// store them. When operating on arbitrary images, [DecodeConfig] should be called
/// before [Decode], so that the program can decide whether the image, as defined
/// in the returned header, can be safely decoded with the available resources. A
/// call to [Decode] which produces an extremely large image, as defined in the
/// header returned by [DecodeConfig], is not considered a security issue,
/// regardless of whether the image is itself malformed or not. A call to
/// [DecodeConfig] which returns a header which does not match the image returned
/// by [Decode] may be considered a security issue, and should be reported per the
/// [Go Security Policy](https://go.dev/security/policy).
using @io {type Slice}
///|
/// Config holds an image's color model and dimensions.
pub(all) struct Config {
color_model : &@color.Model
width : Int
height : Int
}
///|
pub fn Config::new_empty() -> Config {
{ color_model: @color.rgba_model, width: 0, height: 0 }
}
///|
/// Image is a finite rectangular grid of [color.Color] values taken from a color
/// model.
pub(open) trait Image {
/// color_model returns the Image's color model.
fn color_model(Self) -> &@color.Model
/// bounds returns the domain for which At can return non-zero color.
/// The bounds do not necessarily contain the point (0, 0).
fn bounds(Self) -> Rectangle
/// At returns the color of the pixel at (x, y).
/// at(bounds().min.x, bounds().min.y) returns the upper-left pixel of the grid.
/// at(bounds().max.x-1, bounds().max.y-1) returns the lower-right one.
fn at(Self, Int, Int) -> &@color.Color
// other available methods:
fn opaque_(Self) -> Bool
fn set(Self, Int, Int, &@color.Color) -> Unit
fn sub_image(Self, Rectangle) -> &Image
fn as_ycbcr(Self) -> YCbCr?
// Because MoonBit does not have reflection, the following methods must
// also be made available:
fn raw_data(Self) -> Slice[Byte]
fn get_bytes_per_pixel(Self) -> Int
fn get_stride(Self) -> Int
fn pix_offset(Self, Int, Int) -> Int
/// color_index_at returns the palette index of the pixel at (x, y).
/// It returns 0 for non-paletted images.
fn color_index_at(Self, Int, Int) -> Byte
}
///|
pub fn &Image::new_empty() -> &Image {
RGBA::new_empty()
}
///|
pub fn &Image::empty(self : &Image) -> Bool {
self.bounds().empty()
}
///|
/// RGBA64Image is an [Image] whose pixels can be converted directly to a
/// color.RGBA64.
pub(open) trait RGBA64Image {
/// rgba64_at returns the RGBA64 color of the pixel at (x, y). It is
/// equivalent to calling at(x, y).rgba() and converting the resulting
/// 32-bit return values to a color.RGBA64, but it can avoid allocations
/// from converting concrete color types to the color.Color trait type.
fn rgba64_at(Self, Int, Int) -> @color.RGBA64
/// Image trait:
fn color_model(Self) -> &@color.Model
fn bounds(Self) -> Rectangle
fn at(Self, Int, Int) -> &@color.Color
}
///|
suberror SizeError {
SizeError(String)
} derive(Eq)
///|
pub impl Show for SizeError with fn output(self, logger) {
let SizeError(err) = self
logger.write_string(
(
$|SizeError(\{err.escape(quote=true)})
),
)
}
///|
// pixel_buffer_length returns the length of the Slice[Byte] typed pix slice field
// for the Xxx::new functions. Conceptually, this is just (bpp * width * height),
// but this function panics if at least one of those is negative or if the
// computation would overflow the int type.
fn pixel_buffer_length(
bytes_per_pixel : Int,
r : Rectangle,
image_type_name : String,
) -> Int raise SizeError {
let total_length = mul3_non_neg(bytes_per_pixel, r.dx(), r.dy())
if total_length < 0 {
raise SizeError(
"image: \{image_type_name}::new Rectangle has huge or negative dimensions",
)
}
return total_length
}
///|
/// RGBA is an in-memory image whose At method returns [color.RGBA] values.
pub(all) struct RGBA {
// pix holds the image's pixels, in R, G, B, A order. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*4].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `RGBA` satisfies the `Image` trait.
let _RGBA : &Image = RGBA::new_empty()
///|
pub fn RGBA::new_empty() -> RGBA {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for RGBA with fn raw_data(self) {
self.pix
}
///|
pub impl Image for RGBA with fn get_bytes_per_pixel(_self) {
4
}
///|
pub impl Image for RGBA with fn get_stride(self) {
self.stride
}
///|
pub impl Image for RGBA with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for RGBA with fn color_model(_self) {
@color.rgba_model
}
///|
pub impl Image for RGBA with fn bounds(self) {
self.rect
}
///|
pub fn RGBA::op_get(self : RGBA, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for RGBA with fn at(self, x, y) {
self.rgba_at(x, y)
}
///|
pub fn RGBA::rgba64_at(self : RGBA, x : Int, y : Int) -> @color.RGBA64 {
if !pt(x, y).is_in(self.rect) {
return @color.RGBA64::new(0, 0, 0, 0)
}
let i = self.pix_offset(x, y)
let r = self.pix[i + 0].to_uint()
let g = self.pix[i + 1].to_uint()
let b = self.pix[i + 2].to_uint()
let a = self.pix[i + 3].to_uint()
let r = (r << 8) | r
let g = (g << 8) | g
let b = (b << 8) | b
let a = (a << 8) | a
@color.RGBA64::new(r, g, b, a)
}
///|
pub fn RGBA::rgba_at(self : RGBA, x : Int, y : Int) -> @color.RGBA {
if !pt(x, y).is_in(self.rect) {
return @color.RGBA::new(0, 0, 0, 0)
}
let i = self.pix_offset(x, y)
@color.RGBA::new(
self.pix[i + 0],
self.pix[i + 1],
self.pix[i + 2],
self.pix[i + 3],
)
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for RGBA with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 4
}
///|
pub fn RGBA::op_set(self : RGBA, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for RGBA with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let (r, g, b, a) = @color.rgba_model.convert(c).rgba()
self.pix[i + 0] = (r >> 8).to_byte()
self.pix[i + 1] = (g >> 8).to_byte()
self.pix[i + 2] = (b >> 8).to_byte()
self.pix[i + 3] = (a >> 8).to_byte()
}
///|
pub fn RGBA::set_rgba64(
self : RGBA,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = (c.r >> 8).to_byte()
self.pix[i + 1] = (c.g >> 8).to_byte()
self.pix[i + 2] = (c.b >> 8).to_byte()
self.pix[i + 3] = (c.a >> 8).to_byte()
}
///|
pub fn RGBA::set_rgba(self : RGBA, x : Int, y : Int, c : @color.RGBA) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = c.r
self.pix[i + 1] = c.g
self.pix[i + 2] = c.b
self.pix[i + 3] = c.a
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for RGBA with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return RGBA::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
RGBA::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for RGBA with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for RGBA with fn opaque_(self) {
if self.rect.empty() {
return true
}
let mut i0 = 3
let mut i1 = self.rect.dx() * 4
for y = self.rect.min.y; y < self.rect.max.y; y = y + 1 {
for i = i0; i < i1; i = i + 4 {
if self.pix[i] != 0xff {
return false
}
}
i0 += self.stride
i1 += self.stride
}
true
}
///|
/// RGBA::new returns a new [RGBA] image with the given bounds.
pub fn RGBA::new(r : Rectangle) -> RGBA raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(4, r, "RGBA"), b'\x00')),
stride: 4 * r.dx(),
rect: r,
}
}
///|
/// RGBA64 is an in-memory image whose At method returns [color.RGBA64] values.
pub(all) struct RGBA64 {
// pix holds the image's pixels, in R, G, B, A order and big-endian format. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*8].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `RGBA64` satisfies the `Image` trait.
let _RGBA64 : &Image = RGBA64::new_empty()
///|
pub fn RGBA64::new_empty() -> RGBA64 {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for RGBA64 with fn raw_data(self) {
self.pix
}
///|
pub impl Image for RGBA64 with fn get_bytes_per_pixel(_self) {
8
}
///|
pub impl Image for RGBA64 with fn get_stride(self) {
self.stride
}
///|
pub impl Image for RGBA64 with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for RGBA64 with fn color_model(_self) {
@color.rgba64_model
}
///|
pub impl Image for RGBA64 with fn bounds(self) {
self.rect
}
///|
pub fn RGBA64::op_get(self : RGBA64, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for RGBA64 with fn at(self, x, y) {
self.rgba64_at(x, y)
}
///|
pub fn RGBA64::rgba64_at(self : RGBA64, x : Int, y : Int) -> @color.RGBA64 {
if !pt(x, y).is_in(self.rect) {
return @color.RGBA64::new(0, 0, 0, 0)
}
let i = self.pix_offset(x, y)
let r = (self.pix[i + 0].to_uint() << 8) | self.pix[i + 1].to_uint()
let g = (self.pix[i + 2].to_uint() << 8) | self.pix[i + 3].to_uint()
let b = (self.pix[i + 4].to_uint() << 8) | self.pix[i + 5].to_uint()
let a = (self.pix[i + 6].to_uint() << 8) | self.pix[i + 7].to_uint()
{ r, g, b, a }
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for RGBA64 with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 8
}
///|
pub fn RGBA64::op_set(self : RGBA64, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for RGBA64 with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let { r, g, b, a } = @color.RGBA64::from(c)
self.pix[i + 0] = (r >> 8).to_byte()
self.pix[i + 1] = r.to_byte()
self.pix[i + 2] = (g >> 8).to_byte()
self.pix[i + 3] = g.to_byte()
self.pix[i + 4] = (b >> 8).to_byte()
self.pix[i + 5] = b.to_byte()
self.pix[i + 6] = (a >> 8).to_byte()
self.pix[i + 7] = a.to_byte()
}
///|
pub fn RGBA64::set_rgba64(
self : RGBA64,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = (c.r >> 8).to_byte()
self.pix[i + 1] = c.r.to_byte()
self.pix[i + 2] = (c.g >> 8).to_byte()
self.pix[i + 3] = c.g.to_byte()
self.pix[i + 4] = (c.b >> 8).to_byte()
self.pix[i + 5] = c.b.to_byte()
self.pix[i + 6] = (c.a >> 8).to_byte()
self.pix[i + 7] = c.a.to_byte()
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for RGBA64 with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return RGBA64::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
RGBA64::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for RGBA64 with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for RGBA64 with fn opaque_(self) {
if self.rect.empty() {
return true
}
let mut i0 = 6
let mut i1 = self.rect.dx() * 8
for y = self.rect.min.y; y < self.rect.max.y; y = y + 1 {
for i = i0; i < i1; i = i + 8 {
if self.pix[i + 0] != 0xff || self.pix[i + 1] != 0xff {
return false
}
}
i0 += self.stride
i1 += self.stride
}
true
}
///|
/// RGBA64::new returns a new [RGBA64] image with the given bounds.
pub fn RGBA64::new(r : Rectangle) -> RGBA64 raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(8, r, "RGBA64"), b'\x00')),
stride: 8 * r.dx(),
rect: r,
}
}
///|
/// NRGBA is an in-memory image whose At method returns [color.NRGBA] values.
pub(all) struct NRGBA {
// pix holds the image's pixels, in R, G, B, A order. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*4].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `NRGBA` satisfies the `Image` trait.
let _NRGBA : &Image = NRGBA::new_empty()
///|
pub fn NRGBA::new_empty() -> NRGBA {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for NRGBA with fn raw_data(self) {
self.pix
}
///|
pub impl Image for NRGBA with fn get_bytes_per_pixel(_self) {
4
}
///|
pub impl Image for NRGBA with fn get_stride(self) {
self.stride
}
///|
pub impl Image for NRGBA with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for NRGBA with fn color_model(_self) {
@color.nrgba_model
}
///|
pub impl Image for NRGBA with fn bounds(self) {
self.rect
}
///|
pub fn NRGBA::op_get(self : NRGBA, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for NRGBA with fn at(self, x, y) {
self.nrgba_at(x, y)
}
///|
pub fn NRGBA::rgba64_at(self : NRGBA, x : Int, y : Int) -> @color.RGBA64 {
let (r, g, b, a) = self.nrgba_at(x, y).rgba()
@color.RGBA64::new(r, g, b, a)
}
///|
pub fn NRGBA::nrgba_at(self : NRGBA, x : Int, y : Int) -> @color.NRGBA {
if !pt(x, y).is_in(self.rect) {
return @color.NRGBA::new(0, 0, 0, 0)
}
let i = self.pix_offset(x, y)
@color.NRGBA::new(
self.pix[i + 0],
self.pix[i + 1],
self.pix[i + 2],
self.pix[i + 3],
)
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for NRGBA with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 4
}
///|
pub fn NRGBA::op_set(self : NRGBA, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for NRGBA with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let { r, g, b, a } = @color.NRGBA::from(c)
self.pix[i + 0] = r
self.pix[i + 1] = g
self.pix[i + 2] = b
self.pix[i + 3] = a
}
///|
pub fn NRGBA::set_rgba64(
self : NRGBA,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let mut r = c.r
let mut g = c.g
let mut b = c.b
if c.a != 0 && c.a != 0xffff {
r = r * 0xffff / c.a
g = g * 0xffff / c.a
b = b * 0xffff / c.a
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = (r >> 8).to_byte()
self.pix[i + 1] = (g >> 8).to_byte()
self.pix[i + 2] = (b >> 8).to_byte()
self.pix[i + 3] = (c.a >> 8).to_byte()
}
///|
pub fn NRGBA::set_nrgba(
self : NRGBA,
x : Int,
y : Int,
c : @color.NRGBA,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = c.r
self.pix[i + 1] = c.g
self.pix[i + 2] = c.b
self.pix[i + 3] = c.a
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for NRGBA with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return NRGBA::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
NRGBA::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for NRGBA with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for NRGBA with fn opaque_(self) {
if self.rect.empty() {
return true
}
let mut i0 = 3
let mut i1 = self.rect.dx() * 4
for y = self.rect.min.y; y < self.rect.max.y; y = y + 1 {
for i = i0; i < i1; i = i + 4 {
if self.pix[i] != 0xff {
return false
}
}
i0 += self.stride
i1 += self.stride
}
true
}
///|
/// NRGBA::new returns a new [NRGBA] image with the given bounds.
pub fn NRGBA::new(r : Rectangle) -> NRGBA raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(4, r, "NRGBA"), b'\x00')),
stride: 4 * r.dx(),
rect: r,
}
}
///|
/// NRGBA64 is an in-memory image whose At method returns [color.NRGBA64] values.
pub(all) struct NRGBA64 {
// pix holds the image's pixels, in R, G, B, A order and big-endian format. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*8].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `NRGBA64` satisfies the `Image` trait.
let _NRGBA64 : &Image = NRGBA64::new_empty()
///|
pub fn NRGBA64::new_empty() -> NRGBA64 {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for NRGBA64 with fn raw_data(self) {
self.pix
}
///|
pub impl Image for NRGBA64 with fn get_bytes_per_pixel(_self) {
8
}
///|
pub impl Image for NRGBA64 with fn get_stride(self) {
self.stride
}
///|
pub impl Image for NRGBA64 with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for NRGBA64 with fn color_model(_self) {
@color.nrgba64_model
}
///|
pub impl Image for NRGBA64 with fn bounds(self) {
self.rect
}
///|
pub fn NRGBA64::op_get(self : NRGBA64, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for NRGBA64 with fn at(self, x, y) {
self.nrgba64_at(x, y)
}
///|
pub fn NRGBA64::rgba64_at(self : NRGBA64, x : Int, y : Int) -> @color.RGBA64 {
let (r, g, b, a) = self.nrgba64_at(x, y).rgba()
@color.RGBA64::new(r, g, b, a)
}
///|
pub fn NRGBA64::nrgba64_at(self : NRGBA64, x : Int, y : Int) -> @color.NRGBA64 {
if !pt(x, y).is_in(self.rect) {
return @color.NRGBA64::new(0, 0, 0, 0)
}
let i = self.pix_offset(x, y)
let r = (self.pix[i + 0].to_uint() << 8) | self.pix[i + 1].to_uint()
let g = (self.pix[i + 2].to_uint() << 8) | self.pix[i + 3].to_uint()
let b = (self.pix[i + 4].to_uint() << 8) | self.pix[i + 5].to_uint()
let a = (self.pix[i + 6].to_uint() << 8) | self.pix[i + 7].to_uint()
{ r, g, b, a }
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for NRGBA64 with fn pix_offset(self, x, y) {
return (y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 8
}
///|
pub fn NRGBA64::op_set(self : NRGBA64, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for NRGBA64 with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let { r, g, b, a } = @color.NRGBA64::from(c)
self.pix[i + 0] = (r >> 8).to_byte()
self.pix[i + 1] = r.to_byte()
self.pix[i + 2] = (g >> 8).to_byte()
self.pix[i + 3] = g.to_byte()
self.pix[i + 4] = (b >> 8).to_byte()
self.pix[i + 5] = b.to_byte()
self.pix[i + 6] = (a >> 8).to_byte()
self.pix[i + 7] = a.to_byte()
}
///|
pub fn NRGBA64::set_rgba64(
self : NRGBA64,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let mut r = c.r
let mut g = c.g
let mut b = c.b
if c.a != 0 && c.a != 0xffff {
r = r * 0xffff / c.a
g = g * 0xffff / c.a
b = b * 0xffff / c.a
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = (r >> 8).to_byte()
self.pix[i + 1] = r.to_byte()
self.pix[i + 2] = (g >> 8).to_byte()
self.pix[i + 3] = g.to_byte()
self.pix[i + 4] = (b >> 8).to_byte()
self.pix[i + 5] = b.to_byte()
self.pix[i + 6] = (c.a >> 8).to_byte()
self.pix[i + 7] = c.a.to_byte()
}
///|
pub fn NRGBA64::set_nrgba64(
self : NRGBA64,
x : Int,
y : Int,
c : @color.NRGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = (c.r >> 8).to_byte()
self.pix[i + 1] = c.r.to_byte()
self.pix[i + 2] = (c.g >> 8).to_byte()
self.pix[i + 3] = c.g.to_byte()
self.pix[i + 4] = (c.b >> 8).to_byte()
self.pix[i + 5] = c.b.to_byte()
self.pix[i + 6] = (c.a >> 8).to_byte()
self.pix[i + 7] = c.a.to_byte()
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for NRGBA64 with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return NRGBA64::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
NRGBA64::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for NRGBA64 with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for NRGBA64 with fn opaque_(self) {
if self.rect.empty() {
return true
}
let mut i0 = 6
let mut i1 = self.rect.dx() * 8
for y = self.rect.min.y; y < self.rect.max.y; y = y + 1 {
for i = i0; i < i1; i = i + 8 {
if self.pix[i + 0] != 0xff || self.pix[i + 1] != 0xff {
return false
}
}
i0 += self.stride
i1 += self.stride
}
true
}
///|
/// NRGBA64::new returns a new [NRGBA64] image with the given bounds.
pub fn NRGBA64::new(r : Rectangle) -> NRGBA64 raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(8, r, "NRGBA64"), b'\x00')),
stride: 8 * r.dx(),
rect: r,
}
}
///|
/// Alpha is an in-memory image whose At method returns [color.Alpha] values.
pub(all) struct Alpha {
// pix holds the image's pixels, as alpha values. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*1].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `Alpha` satisfies the `Image` trait.
let _Alpha : &Image = Alpha::new_empty()
///|
pub fn Alpha::new_empty() -> Alpha {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for Alpha with fn raw_data(self) {
self.pix
}
///|
pub impl Image for Alpha with fn get_bytes_per_pixel(_self) {
1
}
///|
pub impl Image for Alpha with fn get_stride(self) {
self.stride
}
///|
pub impl Image for Alpha with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for Alpha with fn color_model(_self) {
@color.alpha_model
}
///|
pub impl Image for Alpha with fn bounds(self) {
self.rect
}
///|
pub fn Alpha::op_get(self : Alpha, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for Alpha with fn at(self, x, y) {
self.alpha_at(x, y)
}
///|
pub fn Alpha::rgba64_at(self : Alpha, x : Int, y : Int) -> @color.RGBA64 {
let mut a = self.alpha_at(x, y).a.to_uint()
a = a | (a << 8)
{ r: a, g: a, b: a, a }
}
///|
pub fn Alpha::alpha_at(self : Alpha, x : Int, y : Int) -> @color.Alpha {
if !pt(x, y).is_in(self.rect) {
return @color.Alpha::new(0)
}
let i = self.pix_offset(x, y)
{ a: self.pix[i] }
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for Alpha with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 1
}
///|
pub fn Alpha::op_set(self : Alpha, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for Alpha with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let { a } = @color.Alpha::from(c)
self.pix[i] = a
}
///|
pub fn Alpha::set_rgba64(
self : Alpha,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i] = (c.a >> 8).to_byte()
}
///|
pub fn Alpha::set_alpha(
self : Alpha,
x : Int,
y : Int,
c : @color.Alpha,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i] = c.a
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for Alpha with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return Alpha::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
Alpha::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for Alpha with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for Alpha with fn opaque_(self) {
if self.rect.empty() {
return true
}
let mut i0 = 0
let mut i1 = self.rect.dx()
for y = self.rect.min.y; y < self.rect.max.y; y = y + 1 {
for i = i0; i < i1; i = i + 1 {
if self.pix[i] != 0xff {
return false
}
}
i0 += self.stride
i1 += self.stride
}
return true
}
///|
/// Alpha::new returns a new [Alpha] image with the given bounds.
pub fn Alpha::new(r : Rectangle) -> Alpha raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(1, r, "Alpha"), b'\x00')),
stride: 1 * r.dx(),
rect: r,
}
}
///|
/// Alpha16 is an in-memory image whose At method returns [color.Alpha16] values.
pub(all) struct Alpha16 {
// pix holds the image's pixels, as alpha values in big-endian format. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*2].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `Alpha16` satisfies the `Image` trait.
let _Alpha16 : &Image = Alpha16::new_empty()
///|
pub fn Alpha16::new_empty() -> Alpha16 {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for Alpha16 with fn raw_data(self) {
self.pix
}
///|
pub impl Image for Alpha16 with fn get_bytes_per_pixel(_self) {
2
}
///|
pub impl Image for Alpha16 with fn get_stride(self) {
self.stride
}
///|
pub impl Image for Alpha16 with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for Alpha16 with fn color_model(_self) {
@color.alpha16_model
}
///|
pub impl Image for Alpha16 with fn bounds(self) {
self.rect
}
///|
pub fn Alpha16::op_get(self : Alpha16, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for Alpha16 with fn at(self, x, y) {
self.alpha16_at(x, y)
}
///|
pub fn Alpha16::rgba64_at(self : Alpha16, x : Int, y : Int) -> @color.RGBA64 {
let a = self.alpha16_at(x, y).a
{ r: a, g: a, b: a, a }
}
///|
pub fn Alpha16::alpha16_at(self : Alpha16, x : Int, y : Int) -> @color.Alpha16 {
if !pt(x, y).is_in(self.rect) {
return @color.Alpha16::new(0)
}
let i = self.pix_offset(x, y)
let a = (self.pix[i + 0].to_uint() << 8) | self.pix[i + 1].to_uint()
{ a, }
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for Alpha16 with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 2
}
///|
pub fn Alpha16::op_set(self : Alpha16, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for Alpha16 with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let { a } = @color.Alpha16::from(c)
self.pix[i + 0] = (a >> 8).to_byte()
self.pix[i + 1] = a.to_byte()
}
///|
pub fn Alpha16::set_rgba64(
self : Alpha16,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = (c.a >> 8).to_byte()
self.pix[i + 1] = c.a.to_byte()
}
///|
pub fn Alpha16::set_alpha16(
self : Alpha16,
x : Int,
y : Int,
c : @color.Alpha16,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = (c.a >> 8).to_byte()
self.pix[i + 1] = c.a.to_byte()
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for Alpha16 with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return Alpha16::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
Alpha16::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for Alpha16 with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for Alpha16 with fn opaque_(self) {
if self.rect.empty() {
return true
}
let mut i0 = 0
let mut i1 = self.rect.dx() * 2
for y = self.rect.min.y; y < self.rect.max.y; y = y + 1 {
for i = i0; i < i1; i = i + 2 {
if self.pix[i + 0] != 0xff || self.pix[i + 1] != 0xff {
return false
}
}
i0 += self.stride
i1 += self.stride
}
true
}
///|
/// Alpha16::new returns a new [Alpha16] image with the given bounds.
pub fn Alpha16::new(r : Rectangle) -> Alpha16 raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(2, r, "Alpha16"), b'\x00')),
stride: 2 * r.dx(),
rect: r,
}
}
///|
/// Gray is an in-memory image whose At method returns [color.Gray] values.
pub(all) struct Gray {
// pix holds the image's pixels, as gray values. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*1].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `Gray` satisfies the `Image` trait.
let _Gray : &Image = Gray::new_empty()
///|
pub fn Gray::new_empty() -> Gray {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for Gray with fn raw_data(self) {
self.pix
}
///|
pub impl Image for Gray with fn get_bytes_per_pixel(_self) {
1
}
///|
pub impl Image for Gray with fn get_stride(self) {
self.stride
}
///|
pub impl Image for Gray with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for Gray with fn color_model(_self) {
@color.gray_model
}
///|
pub impl Image for Gray with fn bounds(self) {
self.rect
}
///|
pub fn Gray::op_get(self : Gray, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for Gray with fn at(self, x, y) {
self.gray_at(x, y)
}
///|
pub fn Gray::rgba64_at(self : Gray, x : Int, y : Int) -> @color.RGBA64 {
let mut gray = self.gray_at(x, y).y.to_uint()
gray = gray | (gray << 8)
{ r: gray, g: gray, b: gray, a: 0xffff }
}
///|
pub fn Gray::gray_at(self : Gray, x : Int, y : Int) -> @color.Gray {
if !pt(x, y).is_in(self.rect) {
return @color.Gray::new(0)
}
let i = self.pix_offset(x, y)
{ y: self.pix[i] }
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for Gray with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 1
}
///|
pub fn Gray::op_set(self : Gray, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for Gray with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let { y } = @color.Gray::from(c)
self.pix[i] = y
}
///|
pub fn Gray::set_rgba64(
self : Gray,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
// This formula is the same as in color.grayModel.
let gray = (19595U * c.r + 38470U * c.g + 7471U * c.b + (1U << 15)) >> 24
let i = self.pix_offset(x, y)
self.pix[i] = gray.to_byte()
}
///|
pub fn Gray::set_gray(self : Gray, x : Int, y : Int, c : @color.Gray) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i] = c.y
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for Gray with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return Gray::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
Gray::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for Gray with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for Gray with fn opaque_(_self) {
true
}
///|
/// Gray::new returns a new [Gray] image with the given bounds.
pub fn Gray::new(r : Rectangle) -> Gray raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(1, r, "Gray"), b'\x00')),
stride: 1 * r.dx(),
rect: r,
}
}
///|
/// Gray16 is an in-memory image whose At method returns [color.Gray16] values.
pub(all) struct Gray16 {
// pix holds the image's pixels, as gray values in big-endian format. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*2].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `Gray16` satisfies the `Image` trait.
let _Gray16 : &Image = Gray16::new_empty()
///|
pub fn Gray16::new_empty() -> Gray16 {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for Gray16 with fn raw_data(self) {
self.pix
}
///|
pub impl Image for Gray16 with fn get_bytes_per_pixel(_self) {
2
}
///|
pub impl Image for Gray16 with fn get_stride(self) {
self.stride
}
///|
pub impl Image for Gray16 with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for Gray16 with fn color_model(_self) {
@color.gray16_model
}
///|
pub impl Image for Gray16 with fn bounds(self) {
self.rect
}
///|
pub fn Gray16::op_get(self : Gray16, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for Gray16 with fn at(self, x, y) {
self.gray16_at(x, y)
}
///|
pub fn Gray16::rgba64_at(self : Gray16, x : Int, y : Int) -> @color.RGBA64 {
let gray = self.gray16_at(x, y).y
{ r: gray, g: gray, b: gray, a: 0xffff }
}
///|
pub fn Gray16::gray16_at(self : Gray16, x : Int, y : Int) -> @color.Gray16 {
if !pt(x, y).is_in(self.rect) {
return @color.Gray16::new(0)
}
let i = self.pix_offset(x, y)
let y = (self.pix[i + 0].to_uint() << 8) | self.pix[i + 1].to_uint()
{ y, }
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for Gray16 with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 2
}
///|
pub fn Gray16::op_set(self : Gray16, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for Gray16 with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let { y } = @color.Gray16::from(c)
self.pix[i + 0] = (y >> 8).to_byte()
self.pix[i + 1] = y.to_byte()
}
///|
pub fn Gray16::set_rgba64(
self : Gray16,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
// This formula is the same as in color.gray16Model.
let gray = (19595U * c.r + 38470U * c.g + 7471U * c.b + (1U << 15)) >> 16
let i = self.pix_offset(x, y)
self.pix[i + 0] = (gray >> 8).to_byte()
self.pix[i + 1] = gray.to_byte()
}
///|
pub fn Gray16::set_gray16(
self : Gray16,
x : Int,
y : Int,
c : @color.Gray16,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = (c.y >> 8).to_byte()
self.pix[i + 1] = c.y.to_byte()
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for Gray16 with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return Gray16::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
Gray16::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for Gray16 with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for Gray16 with fn opaque_(_self) {
true
}
///|
/// Gray16::new returns a new [Gray16] image with the given bounds.
pub fn Gray16::new(r : Rectangle) -> Gray16 raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(2, r, "Gray16"), b'\x00')),
stride: 2 * r.dx(),
rect: r,
}
}
///|
/// CMYK is an in-memory image whose At method returns [color.CMYK] values.
pub(all) struct CMYK {
// pix holds the image's pixels, in C, M, Y, K order. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*4].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
}
///|
/// `CMYK` satisfies the `Image` trait.
let _CMYK : &Image = CMYK::new_empty()
///|
pub fn CMYK::new_empty() -> CMYK {
{ pix: Slice::new([]), stride: 0, rect: Rectangle::new() }
}
///|
pub impl Image for CMYK with fn raw_data(self) {
self.pix
}
///|
pub impl Image for CMYK with fn get_bytes_per_pixel(_self) {
4
}
///|
pub impl Image for CMYK with fn get_stride(self) {
self.stride
}
///|
pub impl Image for CMYK with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for CMYK with fn color_model(_self) {
@color.cmyk_model
}
///|
pub impl Image for CMYK with fn bounds(self) {
self.rect
}
///|
pub fn CMYK::op_get(self : CMYK, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for CMYK with fn at(self, x, y) {
self.cmyk_at(x, y)
}
///|
pub fn CMYK::rgba64_at(self : CMYK, x : Int, y : Int) -> @color.RGBA64 {
let (r, g, b, a) = self.cmyk_at(x, y).rgba()
{ r, g, b, a }
}
///|
pub fn CMYK::cmyk_at(self : CMYK, x : Int, y : Int) -> @color.CMYK {
if !pt(x, y).is_in(self.rect) {
return @color.CMYK::new(0, 0, 0, 0)
}
let i = self.pix_offset(x, y)
{
c: self.pix[i + 0],
m: self.pix[i + 1],
y: self.pix[i + 2],
k: self.pix[i + 3],
}
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for CMYK with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 4
}
///|
pub impl Image for CMYK with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
let { c, m, y, k } = @color.CMYK::from(c)
self.pix[i + 0] = c
self.pix[i + 1] = m
self.pix[i + 2] = y
self.pix[i + 3] = k
}
///|
pub fn CMYK::set_rgba64(
self : CMYK,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let (cc, mm, yy, kk) = @color.rgb_to_cmyk(
(c.r >> 8).to_byte(),
(c.g >> 8).to_byte(),
(c.b >> 8).to_byte(),
)
let i = self.pix_offset(x, y)
self.pix[i + 0] = cc
self.pix[i + 1] = mm
self.pix[i + 2] = yy
self.pix[i + 3] = kk
}
///|
pub fn CMYK::set_cmyk(self : CMYK, x : Int, y : Int, c : @color.CMYK) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i + 0] = c.c
self.pix[i + 1] = c.m
self.pix[i + 2] = c.y
self.pix[i + 3] = c.k
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for CMYK with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return CMYK::new_empty()
}
let i = self.pix_offset(r.min.x, r.min.y)
CMYK::{ pix: self.pix[i:], stride: self.stride, rect: r }
}
///|
pub impl Image for CMYK with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for CMYK with fn opaque_(_self) {
true
}
///|
/// CMYK::new returns a new CMYK image with the given bounds.
pub fn CMYK::new(r : Rectangle) -> CMYK raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(4, r, "CMYK"), b'\x00')),
stride: 4 * r.dx(),
rect: r,
}
}
///|
/// YCbCrSubsampling represents the chroma subsampling ratio.
pub(all) enum YCbCrSubsampling {
YCbCrSubsampling444
YCbCrSubsampling422
YCbCrSubsampling420
YCbCrSubsampling440
YCbCrSubsampling411
YCbCrSubsampling410
} derive(Eq)
///|
/// YCbCr is an in-memory image of Y'CbCr colors.
pub(all) struct YCbCr {
y : Slice[Byte]
cb : Slice[Byte]
cr : Slice[Byte]
y_stride : Int
c_stride : Int
subsampling : YCbCrSubsampling
rect : Rectangle
}
///|
pub fn YCbCr::new(
r : Rectangle,
subsampling : YCbCrSubsampling,
) -> YCbCr raise SizeError {
let (w, h) = (r.dx(), r.dy())
let (cw, ch) = match subsampling {
YCbCrSubsampling444 => (w, h)
YCbCrSubsampling422 => ((w + 1) / 2, h)
YCbCrSubsampling420 => ((w + 1) / 2, (h + 1) / 2)
YCbCrSubsampling440 => (w, (h + 1) / 2)
YCbCrSubsampling411 => ((w + 3) / 4, h)
YCbCrSubsampling410 => ((w + 3) / 4, (h + 1) / 2)
}
let y_len = pixel_buffer_length(1, r, "YCbCr.Y")
let c_len = mul3_non_neg(1, cw, ch)
if c_len < 0 {
raise SizeError(
"image: YCbCr::new Rectangle has huge or negative dimensions",
)
}
{
y: Slice::new(Array::make(y_len, b'\x00')),
cb: Slice::new(Array::make(c_len, b'\x00')),
cr: Slice::new(Array::make(c_len, b'\x00')),
y_stride: w,
c_stride: cw,
subsampling,
rect: r,
}
}
///|
pub impl Image for YCbCr with fn color_model(_self) {
@color.y_cb_cr_model
}
///|
pub impl Image for YCbCr with fn bounds(self) {
self.rect
}
///|
pub impl Image for YCbCr with fn at(self, x, y) {
if !pt(x, y).is_in(self.rect) {
return @color.black
}
let yi = (y - self.rect.min.y) * self.y_stride + (x - self.rect.min.x)
let (cx, cy) = match self.subsampling {
YCbCrSubsampling444 => (x - self.rect.min.x, y - self.rect.min.y)
YCbCrSubsampling422 => ((x - self.rect.min.x) / 2, y - self.rect.min.y)
YCbCrSubsampling420 =>
((x - self.rect.min.x) / 2, (y - self.rect.min.y) / 2)
YCbCrSubsampling440 => (x - self.rect.min.x, (y - self.rect.min.y) / 2)
YCbCrSubsampling411 => ((x - self.rect.min.x) / 4, y - self.rect.min.y)
YCbCrSubsampling410 =>
((x - self.rect.min.x) / 4, (y - self.rect.min.y) / 2)
}
let ci = cy * self.c_stride + cx
@color.YCbCr::new(self.y[yi], self.cb[ci], self.cr[ci])
}
///|
pub impl Image for YCbCr with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let yi = (y - self.rect.min.y) * self.y_stride + (x - self.rect.min.x)
let (cx, cy) = match self.subsampling {
YCbCrSubsampling444 => (x - self.rect.min.x, y - self.rect.min.y)
YCbCrSubsampling422 => ((x - self.rect.min.x) / 2, y - self.rect.min.y)
YCbCrSubsampling420 =>
((x - self.rect.min.x) / 2, (y - self.rect.min.y) / 2)
YCbCrSubsampling440 => (x - self.rect.min.x, (y - self.rect.min.y) / 2)
YCbCrSubsampling411 => ((x - self.rect.min.x) / 4, y - self.rect.min.y)
YCbCrSubsampling410 =>
((x - self.rect.min.x) / 4, (y - self.rect.min.y) / 2)
}
let ci = cy * self.c_stride + cx
let yc : @color.YCbCr = @color.YCbCr::from(c)
self.y[yi] = yc.y
self.cb[ci] = yc.cb
self.cr[ci] = yc.cr
}
///|
pub impl Image for YCbCr with fn raw_data(self) {
self.y
}
///|
pub impl Image for YCbCr with fn get_bytes_per_pixel(_self) {
1
}
///|
pub impl Image for YCbCr with fn get_stride(self) {
self.y_stride
}
///|
pub impl Image for YCbCr with fn color_index_at(_self, _x, _y) {
0
}
///|
pub impl Image for YCbCr with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.y_stride + (x - self.rect.min.x)
}
///|
pub impl Image for YCbCr with fn opaque_(_self) {
true
}
///|
pub impl Image for YCbCr with fn sub_image(self, r) {
let r = r.intersect(self.rect)
if r.empty() {
return YCbCr::{
y: Slice::new([]),
cb: Slice::new([]),
cr: Slice::new([]),
y_stride: 0,
c_stride: 0,
subsampling: self.subsampling,
rect: Rectangle::new(),
}
}
let yi = self.pix_offset(r.min.x, r.min.y)
let (cx, cy) = match self.subsampling {
YCbCrSubsampling444 =>
(r.min.x - self.rect.min.x, r.min.y - self.rect.min.y)
YCbCrSubsampling422 =>
((r.min.x - self.rect.min.x) / 2, r.min.y - self.rect.min.y)
YCbCrSubsampling420 =>
((r.min.x - self.rect.min.x) / 2, (r.min.y - self.rect.min.y) / 2)
YCbCrSubsampling440 =>
(r.min.x - self.rect.min.x, (r.min.y - self.rect.min.y) / 2)
YCbCrSubsampling411 =>
((r.min.x - self.rect.min.x) / 4, r.min.y - self.rect.min.y)
YCbCrSubsampling410 =>
((r.min.x - self.rect.min.x) / 4, (r.min.y - self.rect.min.y) / 2)
}
let ci = cy * self.c_stride + cx
YCbCr::{
y: self.y[yi:],
cb: self.cb[ci:],
cr: self.cr[ci:],
y_stride: self.y_stride,
c_stride: self.c_stride,
subsampling: self.subsampling,
rect: r,
}
}
///|
pub impl Image for YCbCr with fn as_ycbcr(self) {
Some(self)
}
///|
/// Paletted is an in-memory image of Byte indices into a given palette.
pub(all) struct Paletted {
// pix holds the image's pixels, as palette indices. The pixel at
// (x, y) starts at pix[(y-rect.min.y)*stride + (x-rect.min.x)*1].
pix : Slice[Byte]
// stride is the pix stride (in bytes) between vertically adjacent pixels.
stride : Int
// rect is the image's bounds.
rect : Rectangle
// Palette is the image's palette.
mut palette : @color.Palette
}
///|
/// `Paletted` satisfies the `Image` trait.
let _Paletted : &Image = Paletted::new_empty()
///|
pub impl Image for Paletted with fn raw_data(self) {
self.pix
}
///|
pub impl Image for Paletted with fn get_bytes_per_pixel(_self) {
1
}
///|
pub impl Image for Paletted with fn get_stride(self) {
self.stride
}
///|
pub impl Image for Paletted with fn color_model(self) {
self.palette
}
///|
pub impl Image for Paletted with fn bounds(self) {
self.rect
}
///|
pub fn Paletted::op_get(self : Paletted, p : Point) -> &@color.Color {
self.at(p.x, p.y)
}
///|
pub impl Image for Paletted with fn at(self, x, y) {
if self.palette.0.length() == 0 {
return @color.black // nil
}
if !pt(x, y).is_in(self.rect) {
return self.palette.0[0]
}
let i = self.pix_offset(x, y)
return self.palette.0[self.pix[i].to_int()]
}
///|
pub fn Paletted::rgba64_at(self : Paletted, x : Int, y : Int) -> @color.RGBA64 {
if self.palette.0.length() == 0 {
return @color.RGBA64::new(0, 0, 0, 0)
}
let mut c : &@color.Color = @color.black
if !pt(x, y).is_in(self.rect) {
c = self.palette.0[0]
} else {
let i = self.pix_offset(x, y)
c = self.palette.0[self.pix[i].to_int()]
}
let (r, g, b, a) = c.rgba()
{ r, g, b, a }
}
///|
/// pix_offset returns the index of the first element of pix that corresponds to
/// the pixel at (x, y).
pub impl Image for Paletted with fn pix_offset(self, x, y) {
(y - self.rect.min.y) * self.stride + (x - self.rect.min.x) * 1
}
///|
pub fn Paletted::op_set(self : Paletted, p : Point, c : &@color.Color) -> Unit {
self.set(p.x, p.y, c)
}
///|
pub impl Image for Paletted with fn set(self, x, y, c) {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i] = self.palette.index(c).to_byte()
}
///|
pub fn Paletted::set_rgba64(
self : Paletted,
x : Int,
y : Int,
c : @color.RGBA64,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i] = self.palette.index(c).to_byte()
}
///|
pub impl Image for Paletted with fn color_index_at(self, x, y) {
if !pt(x, y).is_in(self.rect) {
return 0
}
let i = self.pix_offset(x, y)
self.pix[i]
}
///|
pub fn Paletted::set_color_index(
self : Paletted,
x : Int,
y : Int,
index : Byte,
) -> Unit {
if !pt(x, y).is_in(self.rect) {
return
}
let i = self.pix_offset(x, y)
self.pix[i] = index
}
///|
/// sub_image returns an image representing the portion of the image p visible
/// through r. The returned value shares pixels with the original image.
pub impl Image for Paletted with fn sub_image(self, r) {
let r = r.intersect(self.rect)
// If r1 and r2 are Rectangles, r1.intersect(r2) is not guaranteed to be inside
// either r1 or r2 if the intersection is empty. Without explicitly checking for
// this, the pix[i:] expression below can panic.
if r.empty() {
return Paletted::{
pix: Slice::new([]),
stride: 0,
rect: Rectangle::new(),
palette: self.palette,
}
}
let i = self.pix_offset(r.min.x, r.min.y)
Paletted::{
pix: self.pix[i:],
stride: self.stride,
rect: self.rect.intersect(r),
palette: self.palette,
}
}
///|
pub impl Image for Paletted with fn as_ycbcr(_self) {
None
}
///|
/// opaque scans the entire image and reports whether it is fully opaque.
pub impl Image for Paletted with fn opaque_(self) {
let present = Array::make(256, false)
let mut i0 = 0
let mut i1 = self.rect.dx()
for y = self.rect.min.y; y < self.rect.max.y; y = y + 1 {
for c in self.pix[i0:i1] {
present[c.to_int()] = true
}
i0 += self.stride
i1 += self.stride
}
for i, c in self.palette.0 {
if !present[i] {
continue
}
let (_, _, _, a) = c.rgba()
if a != 0xffff {
return false
}
}
true
}
///|
/// Paletted::new returns a new [Paletted] image with the given width, height and
/// palette.
pub fn Paletted::new(
r : Rectangle,
p : @color.Palette,
) -> Paletted raise SizeError {
{
pix: Slice::new(Array::make(pixel_buffer_length(1, r, "Paletted"), b'\x00')),
stride: 1 * r.dx(),
rect: r,
palette: p,
}
}
///|
pub fn Paletted::new_empty() -> Paletted {
{
pix: Slice::new([]),
stride: 0,
rect: Rectangle::new(),
palette: @color.Palette::new_empty(),
}
}