A class that represents a Bitmap image
bitmap :: Bitmap :: defaultinit
bitmap :: Bitmap :: file_extension
The file extension if this Bitmap is created by loading a bitmap filebitmap :: Bitmap :: file_extension=
The file extension if this Bitmap is created by loading a bitmap filebitmap :: Bitmap :: file_path=
The file path if this Bitmap is created by loading a bitmap filebitmap :: Bitmap :: file_size=
The file size if this Bitmap is created by loading a bitmap filecore :: Object :: class_factory
Implementation used byget_class
to create the specific class.
core :: Object :: defaultinit
bitmap :: Bitmap :: defaultinit
bitmap :: Bitmap :: file_extension
The file extension if this Bitmap is created by loading a bitmap filebitmap :: Bitmap :: file_extension=
The file extension if this Bitmap is created by loading a bitmap filebitmap :: Bitmap :: file_path=
The file path if this Bitmap is created by loading a bitmap filebitmap :: Bitmap :: file_size=
The file size if this Bitmap is created by loading a bitmap filecore :: Object :: is_same_instance
Return true ifself
and other
are the same instance (i.e. same identity).
core :: Object :: is_same_serialized
Isself
the same as other
in a serialization context?
core :: Object :: is_same_type
Return true ifself
and other
have the same dynamic type.
core :: Object :: output_class_name
Display class name on stdout (debug only).
#
# A class that represents a Bitmap image
class Bitmap
# The file path if this Bitmap is created by loading a bitmap file
var file_path: String is noinit
# The file extension if this Bitmap is created by loading a bitmap file
var file_extension: String is noinit
# The file size if this Bitmap is created by loading a bitmap file
var file_size: Int is noinit
# The image width in pixels
var width: Int is noinit
# The image height in pixels
var height: Int is noinit
# The offset of image data. Typically, 54
private var data_offset: Int is noinit
# The number of bits representing a pixel. Currently only 24-bit bitmaps are supported
private var bits_per_pixel: Int is noinit
# The size of images. For a 24-bit bitmap, this is equal to (3*width*height)
private var image_size: Int is noinit
# 14-byte bitmap header
private var bitmap_header = [66, 77, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0]
# 40-byte dib header
private var dib_header = [40, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 24, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
# The image data, each cell contains a pixel.
# The first 8 least-significant bits represent the red component,
# the next 8 least-significant bits represent the green component,
# and the next 8 bit represent the blue component.
# The last 8 most-significant bits are not used
private var data = new Array[Array[Int]]
# Constructs a Bitmap object by passing the image's width and height (in pixels)
init with_size(width: Int, height: Int)
do
self.width = width
self.height = height
self.bits_per_pixel = 24
self.data_offset = 54
#set width and height
self.set_value(dib_header, 4, width)
self.set_value(dib_header, 8, height)
#set file size
var file_size = 3 * width * height + 54
self.set_value(bitmap_header, 2, file_size)
#init pixel data
self.data = new Array[Array[Int]].with_capacity(height)
for x in [0..height[ do
var row = new Array[Int].with_capacity(width)
for y in [0..width[ do
row.add(0x00FFFFFF)
end
self.data[x] = row
end
end
# Creates an instance of Bitmap by loading an existing image file
init load(path: String)
do
self.file_path = path
var temp = path.file_extension
if temp != null then
self.file_extension = temp
else
self.file_extension = ""
end
var fileReader = new FileReader.open(path)
# =============== Bitmap header ================
for x in [0..13] do
var b = fileReader.read_byte
if b < 0 then return
bitmap_header[x] = b
end
self.file_size = get_value(bitmap_header.subarray(2, 4))
self.data_offset = get_value(bitmap_header.subarray(10, 4))
# =============== DIB header ================
for x in [0..39] do
var b = fileReader.read_byte
if b < 0 then return
dib_header[x] = b
end
var dib_size = get_value(dib_header.subarray(0, 4))
# only support BITMAPINFOHEADER
if dib_size != 40 then
print "This type of bitmap is not supported"
fileReader.close
return
end
self.width = get_value(dib_header.subarray(4, 4))
self.height = get_value(dib_header.subarray(8, 4))
self.bits_per_pixel = get_value(dib_header.subarray(14, 2))
self.image_size = get_value(dib_header.subarray(20, 4))
if self.bits_per_pixel != 24 then
print "Only full color bitmaps are supported"
fileReader.close
return
end
if self.bits_per_pixel == 24 then
# assert self.image_size + 54 <= self.file_size
# start loading image data, for now assume no padding
for x in [0..self.height[
do
var row = new Array[Int].with_capacity(self.width)
var rgb_str = new CString(3)
for y in [0..self.width[
do
var bts = fileReader.read_bytes_to_cstring(rgb_str, 3)
if bts < 3 then return
var red = rgb_str[0] << 16
var green = rgb_str[1] << 8
var blue = rgb_str[2]
row.add(red.to_i + green.to_i + blue.to_i)
end
self.data.add(row)
end
end
fileReader.close
end #end of load_from_file method
# Converts the value contained in two or four bytes into an Int. Since Nit
# does not have a byte type, Int is used
private fun get_value(array: Array[Int]): Int
do
var value = 0
for x in [0..array.length[ do
value += array[x] * 256.to_f.pow(x.to_f).to_i
end
return value
end
# Converts the value in an Int to four bytes. Since Nit does not have a byte
# type, Int is used.
private fun set_value(array: Array[Int], start_index: Int, value: Int)
do
array[start_index] = value & 0x000000FF
array[start_index + 1] = (value >> 8) & 0x000000FF
array[start_index + 2] = (value >> 16) & 0x000000FF
array[start_index + 3] = (value >> 24) & 0x000000FF
end
# Saves the bitmap into a file
fun save(path: String)
do
var fw = new FileWriter.open(path)
# Write bitmap header
for x in [0..self.bitmap_header.length[ do
fw.write(self.bitmap_header[x].code_point.to_s)
end
# Write dib header
for x in [0..self.dib_header.length[ do
fw.write(self.dib_header[x].code_point.to_s)
end
# Write color table (if any)
# Write data (no padding for now)
for x in [0..self.height[ do
var row = self.data[x]
for y in [0..self.width[ do
var pixel = row[y]
var red = pixel >> 16
var green = (pixel & 0x00FF00) >> 8
var blue = pixel & 0x000000FF
fw.write(red.code_point.to_s)
fw.write(green.code_point.to_s)
fw.write(blue.code_point.to_s)
end
end
fw.close
end #end of save
# Converts the bitmap to grayscale by manipulating each individual pixel
fun grayscale
do
for x in [0..self.height[ do
var row = self.data[x]
for y in [0..self.width[ do
var pixel = row[y]
var red = pixel >> 16
var green = (pixel & 0x00FF00) >> 8
var blue = pixel & 0x000000FF
var lum = (0.2126 * red.to_f + 0.7152 * green.to_f + 0.0722 * blue.to_f).to_i
pixel = lum * 256 * 256 + lum * 256 + lum
self.data[x][y] = pixel
end
end
end
# Sets an individual pixel, where position (0, 0) represents the top-left pixel.
fun set_pixel(x: Int, y: Int, color: Int)
do
if x >= 0 and y >= 0 and x < self.width and y < self.height then
# Since a bitmap stores its rows of pixels upside-down, y is mapped to
# height - 1 - y to make (0, 0) the top-left pixel
self.data[self.height - 1 - y][x] = color
end
end
end
lib/bitmap/bitmap.nit:49,1--262,3