| import 'dart:typed_data'; |
| |
| import '../image.dart'; |
| import 'hdr_slice.dart'; |
| |
| /** |
| * A high dynamic range RGBA image stored in 16-bit or 32-bit floating-point |
| * channels. |
| */ |
| class HdrImage { |
| static const int HALF = 1; |
| static const int FLOAT = 2; |
| static const int UINT = 0; |
| |
| /// Red value of a sample |
| static const String R = 'R'; |
| /// Green value of a sample |
| static const String G = 'G'; |
| /// Blue value of a sample |
| static const String B = 'B'; |
| /// Alpha/opacity |
| static const String A = 'A'; |
| /// Distance of the front of a sample from the viewer |
| static const String Z = 'Z'; |
| /// A numerical identifier for the object represented by a sample. |
| static const String ID = 'id'; |
| |
| final Map<String, HdrSlice> slices = {}; |
| HdrSlice red; |
| HdrSlice green; |
| HdrSlice blue; |
| HdrSlice alpha; |
| HdrSlice depth; |
| |
| HdrImage() { |
| } |
| |
| /** |
| * Create an RGB[A] image. |
| */ |
| HdrImage.create(int width, int height, int channels, int format) { |
| if (channels < 0 || channels > 4) { |
| return; |
| } |
| if (format != HALF && format != FLOAT && format != UINT) { |
| return; |
| } |
| |
| const List<String> channelList = const [R, G, B, A]; |
| for (int i = 0; i < channels; ++i) { |
| addSlice(new HdrSlice(channelList[i], width, height, format)); |
| } |
| } |
| |
| /** |
| * Create a copy of the [other] HdrImage. |
| */ |
| HdrImage.from(HdrImage other) { |
| for (String ch in other.slices.keys) { |
| HdrSlice slice = other.slices[ch]; |
| addSlice(new HdrSlice.from(slice)); |
| } |
| } |
| |
| /** |
| * Create an HDR image from a LDR [Image] by transforming the channel values |
| * to the range [0, 1]. |
| */ |
| HdrImage.fromImage(Image other) { |
| addSlice(new HdrSlice(R, other.width, other.height, HALF)); |
| addSlice(new HdrSlice(G, other.width, other.height, HALF)); |
| addSlice(new HdrSlice(B, other.width, other.height, HALF)); |
| if (other.format == Image.RGBA) { |
| addSlice(new HdrSlice(A, other.width, other.height, HALF)); |
| } |
| Uint8List rgb = other.getBytes(); |
| for (int y = 0, si = 0; y < other.height; ++y) { |
| for (int x = 0; x < other.width; ++x) { |
| red.setFloat(x, y, rgb[si++] / 255.0); |
| green.setFloat(x, y, rgb[si++] / 255.0); |
| blue.setFloat(x, y, rgb[si++] / 255.0); |
| if (alpha != null) { |
| alpha.setFloat(x, y, rgb[si++] / 255.0); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Does the image have any color channels? |
| */ |
| bool get hasColor => red != null || green != null || blue != null; |
| |
| /** |
| * Does the image have an alpha channel? |
| */ |
| bool get hasAlpha => alpha != null; |
| |
| /** |
| * Does the image have a depth channel? |
| */ |
| bool get hasDepth => depth != null; |
| |
| /** |
| * The width of the framebuffer. |
| */ |
| int get width => slices.isEmpty ? 0 : slices.values.first.width; |
| |
| /** |
| * The height of the framebuffer. |
| */ |
| int get height => slices.isEmpty ? 0 : slices.values.first.height; |
| |
| /** |
| * Get the value of the red channel at the given pixel coordinates [x], [y]. |
| */ |
| double getRed(int x, int y) { |
| return red != null ? red.getFloat(x, y) : 0.0; |
| } |
| |
| /** |
| * Set the value of the red channel at the given pixel coordinates [x], [y]. |
| */ |
| void setRed(int x, int y, double c) { |
| if (red != null) { |
| red.setFloat(x, y, c); |
| } |
| } |
| |
| void setRedInt(int x, int y, int c) { |
| if (red != null) { |
| red.setInt(x, y, c); |
| } |
| } |
| |
| /** |
| * Get the value of the green channel at the given pixel coordinates [x], [y]. |
| */ |
| double getGreen(int x, int y) { |
| return green != null ? green.getFloat(x, y) : 0.0; |
| } |
| |
| /** |
| * Set the value of the green channel at the given pixel coordinates [x], [y]. |
| */ |
| void setGreen(int x, int y, double c) { |
| if (green != null) { |
| green.setFloat(x, y, c); |
| } |
| } |
| |
| void setGreenInt(int x, int y, int c) { |
| if (green != null) { |
| green.setInt(x, y, c); |
| } |
| } |
| |
| /** |
| * Get the value of the blue channel at the given pixel coordinates [x], [y]. |
| */ |
| double getBlue(int x, int y) { |
| return blue != null ? blue.getFloat(x, y) : 0.0; |
| } |
| |
| /** |
| * Set the value of the blue channel at the given pixel coordinates [x], [y]. |
| */ |
| void setBlue(int x, int y, double c) { |
| if (blue != null) { |
| blue.setFloat(x, y, c); |
| } |
| } |
| |
| void setBlueInt(int x, int y, int c) { |
| if (blue != null) { |
| blue.setInt(x, y, c); |
| } |
| } |
| |
| /** |
| * Get the value of the alpha channel at the given pixel coordinates [x], [y]. |
| */ |
| double getAlpha(int x, int y) { |
| return alpha != null ? alpha.getFloat(x, y) : 0.0; |
| } |
| |
| /** |
| * Set the value of the alpha channel at the given pixel coordinates [x], [y]. |
| */ |
| void setAlpha(int x, int y, double c) { |
| if (alpha != null) { |
| alpha.setFloat(x, y, c); |
| } |
| } |
| |
| void setAlphaInt(int x, int y, int c) { |
| if (alpha != null) { |
| alpha.setInt(x, y, c); |
| } |
| } |
| |
| /** |
| * Get the value of the depth channel at the given pixel coordinates [x], [y]. |
| */ |
| double getDepth(int x, int y) { |
| return depth != null ? depth.getFloat(x, y) : 0.0; |
| } |
| |
| /** |
| * Set the value of the depth channel at the given pixel coordinates [x], [y]. |
| */ |
| void setDepth(int x, int y, double c) { |
| if (depth != null) { |
| depth.setFloat(x, y, c); |
| } |
| } |
| |
| /** |
| * Does this image contain the given channel? |
| */ |
| bool hasChannel(String ch) => slices.containsKey(ch); |
| |
| /** |
| * Access a framebuffer slice by name. |
| */ |
| HdrSlice operator[](String ch) => slices[ch]; |
| |
| /** |
| * Add a channel [slice] to the |
| */ |
| void addSlice(HdrSlice slice) { |
| String ch = slice.name; |
| slices[ch] = slice; |
| switch (ch) { |
| case R: |
| red = slice; |
| break; |
| case G: |
| green = slice; |
| break; |
| case B: |
| blue = slice; |
| break; |
| case A: |
| alpha = slice; |
| break; |
| case Z: |
| depth = slice; |
| break; |
| } |
| } |
| |
| /** |
| * Convert the framebuffer to an floating-point image, as a sequence of |
| * floats in RGBA order. |
| */ |
| Float32List toFloatRgba() { |
| Float32List rgba = new Float32List(width * height * 4); |
| int w = width; |
| int h = height; |
| for (int y = 0, di = 0; y < h; ++y) { |
| for (int x = 0; x < w; ++x) { |
| rgba[di++] = red == null ? 0.0 : red.getFloat(x, y); |
| rgba[di++] = green == null ? 0.0 : green.getFloat(x, y); |
| rgba[di++] = blue == null ? 0.0 : blue.getFloat(x, y); |
| rgba[di++] = alpha == null ? 1.0 : alpha.getFloat(x, y); |
| } |
| } |
| |
| return rgba; |
| } |
| } |
| |