diff options
author | Aleksey Lim <alsroot@member.fsf.org> | 2009-02-18 03:58:55 (GMT) |
---|---|---|
committer | Aleksey Lim <alsroot@member.fsf.org> | 2009-02-20 00:03:07 (GMT) |
commit | b999a05ad78264d6b7975bf39c8297f36a514ed1 (patch) | |
tree | 9d635b80737206ccea9aa7e448910b93760116a9 /src/canvas.h | |
parent | 25ddc1089bb0950d2d8a8cec8a62acb1a9469ec2 (diff) |
Add Makefile to build binary package in convenient way
Diffstat (limited to 'src/canvas.h')
-rw-r--r-- | src/canvas.h | 1846 |
1 files changed, 0 insertions, 1846 deletions
diff --git a/src/canvas.h b/src/canvas.h deleted file mode 100644 index bbed722..0000000 --- a/src/canvas.h +++ /dev/null @@ -1,1846 +0,0 @@ -/* - Copyright 2008 by Jens Andersson and Wade Brainerd. - This file is part of Colors! XO. - - Colors is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Colors is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Colors. If not, see <http://www.gnu.org/licenses/>. -*/ -#ifndef _CANVAS_H_ -#define _CANVAS_H_ - -#include "colorsc.h" -#include "drwfile.h" - -// Uncomment this to print all executed drawing commands to stdout. -//#define CANVAS_DEBUG_COMMANDS - -// Structure for passing pixel data to and from Python. -struct SurfaceA8R8G8B8 -{ - int width, height; - int stride; - unsigned int* pixels; -}; - -// Structure for passing buffers of draw commands to and from Python. -struct DrawCommandBuffer -{ - DrawCommandBuffer() - { - cmds = NULL; - ncommands = 0; - } - - DrawCommandBuffer(const char* _cmds, int _ncommands) - { - cmds = (char*)malloc(_ncommands*sizeof(unsigned int)); - memcpy(cmds, _cmds, _ncommands*sizeof(unsigned int)); - ncommands = _ncommands; - } - - DrawCommandBuffer(const DrawCommandBuffer& b) - { - cmds = (char*)malloc(b.ncommands*sizeof(unsigned int)); - memcpy(cmds, b.cmds, b.ncommands*sizeof(unsigned int)); - ncommands = b.ncommands; - } - - ~DrawCommandBuffer() - { - if (cmds) - { - free((void*)cmds); - cmds = NULL; - } - } - - const DrawCommandBuffer& operator=(const DrawCommandBuffer& b) - { - if (cmds) - free((void*)cmds); - cmds = (char*)malloc(b.ncommands*sizeof(unsigned int)); - memcpy(cmds, b.cmds, b.ncommands*sizeof(unsigned int)); - ncommands = b.ncommands; - return *this; - } - - void append(const DrawCommandBuffer& b) - { - char* newcmds = (char*)malloc((ncommands+b.ncommands)*sizeof(unsigned int)); - if (cmds) - { - memcpy(newcmds, cmds, ncommands*sizeof(unsigned int)); - free(cmds); - } - memcpy(newcmds + ncommands*sizeof(unsigned int), b.cmds, b.ncommands*sizeof(unsigned int)); - cmds = newcmds; - ncommands = ncommands + b.ncommands; - } - - void clear() - { - if (cmds) - { - free(cmds); - cmds = NULL; - } - ncommands = 0; - } - - ByteBuffer get_bytes() - { - ByteBuffer buf; - buf.size = ncommands*sizeof(unsigned int); - buf.data = cmds; - return buf; - } - - char* cmds; - int ncommands; - - static DrawCommandBuffer create_from_string(const char* cmds, int ncommands) - { - return DrawCommandBuffer(cmds, ncommands); - } -}; - -struct DrawCommand -{ - enum - { - TYPE_DRAW = 0, - TYPE_DRAWEND = 1, - TYPE_COLORCHANGE = 2, - TYPE_SIZECHANGE = 3, - }; - - int type; - Pos pos; - Color color; - int pressure; - bool flipx; - bool flipy; - bool is_text; - uint32_t text; - int brush_control; - int brush_type; - float size; - float opacity; - - DrawCommand() - { - type = TYPE_DRAW; - pressure = 0; - flipx = false; - flipy = false; - is_text = false; - text = 0; - brush_control = 0; - brush_type = 0; - size = 0; - opacity = 0; - } - - static DrawCommand create_color_change(const Color& c) - { - DrawCommand cmd; - cmd.type = TYPE_COLORCHANGE; - cmd.color = c; - return cmd; - } - - static DrawCommand create_draw(const Pos& pos, int pressure) - { - DrawCommand cmd; - cmd.type = TYPE_DRAW; - cmd.pos = pos; - cmd.pressure = pressure; - return cmd; - } - - static DrawCommand create_end_draw(int pressure) - { - DrawCommand cmd; - cmd.type = TYPE_DRAWEND; - cmd.pressure = pressure; - return cmd; - } - - static DrawCommand create_size_change(int brush_control, int brush_type, float size, float opacity) - { - DrawCommand cmd; - cmd.type = TYPE_SIZECHANGE; - cmd.brush_control = brush_control; - cmd.brush_type = brush_type; - cmd.size = size; - cmd.opacity = opacity; - return cmd; - } - - static DrawCommand create_flip(bool flipx) - { - DrawCommand cmd; - cmd.type = TYPE_COLORCHANGE; - cmd.flipx = flipx; - cmd.flipy = !flipx; - return cmd; - } -}; - -struct BrushType -{ - enum - { - BRUSHTYPE_HARD = 0, - BRUSHTYPE_SOFT = 1, - BRUSHTYPE_CURSOR = 2, - NUM_BRUSHES = 3, - }; - - static const int DIST_TABLE_WIDTH = 256; // Width of distance lookup-table - static const int DIST_TABLE_CENTER = DIST_TABLE_WIDTH / 2; // Center of distance lookup-table - - static const int BRUSH_TABLE_WIDTH = 256; // Width of brush lookup-table - static const int BRUSH_TABLE_HEIGHT = 65; // Height of 65 allows a brushsize down to 1.0f - - static const float EXTRA_BRUSH_SCALE = 1.023f; // Scales down the brush-size so we don't index-out-of-range. - - static unsigned char distance_tbl[DIST_TABLE_WIDTH][DIST_TABLE_WIDTH]; - - unsigned char intensity_tbl[BRUSH_TABLE_WIDTH][BRUSH_TABLE_HEIGHT]; - - // Creates a simple sqrt-lookup table. - static void create_distance_table() - { - for (int x = 0; x < DIST_TABLE_WIDTH; x++) - for (int y = 0; y < DIST_TABLE_WIDTH; y++) - { - int dx = x - DIST_TABLE_CENTER; - int dy = y - DIST_TABLE_CENTER; - float dist = sqrtf(float(dx * dx + dy * dy)); - distance_tbl[x][y] = (unsigned char)min(255.0f, dist*255/DIST_TABLE_CENTER); - } - } - - // Calculates a gradient between 0 and 1 where the derivate of both 0 and 1 is 0. - // This function is used to calculate the falloff of the brushes. - float smooth_step(float a) - { - return sinf((a*a - 0.5f) * 3.14159f) * 0.5f + 0.5f; - } - - void create_brush(float brush_border, float amp) - { - // Find at what range from brush-center the brush intensity goes below 2 - float max_r = 0; - for (int i = BRUSH_TABLE_WIDTH-1; i >= 0; i--) - { - float f = float(i) / BRUSH_TABLE_WIDTH; - float f2 = 1.0f - (f - brush_border) / (1.0f - brush_border); - if (round(smooth_step(f2) * amp) >= 2) - { - max_r = i; - break; - } - } - - // Calculate a scale-factor so the brush optimally uses the area - float r = float(max_r + 2) / BRUSH_TABLE_WIDTH / BRUSH_TABLE_WIDTH; - - for (int y = 0; y < BRUSH_TABLE_HEIGHT; y++) - { - // Each line in the brush-table is calculated for a specific brush-size - // This has two functions: - // 1. Be able to simulate the effect of resampling of the "perfect" big brush to a smaller one to improve precision - // 2. Compensate for the need to scale small brushes to avoid index out of range during rastering - - // Calculate scale for this width - float brushscale = EXTRA_BRUSH_SCALE + y * 2.0f / 64.0f; - - // Calculate brush - unsigned int intensity_row[BRUSH_TABLE_WIDTH]; - for (int i = 0; i < BRUSH_TABLE_WIDTH; i++) - { - float f = min(i * r * brushscale, 1.0f); // Apply the two different scales - if (f < brush_border) - intensity_row[i] = int(amp); - else - { - float f2 = 1.0f - (f - brush_border) / (1.0f - brush_border); - f2 = smooth_step(f2) * amp; // Make sure the border-falloff is smooth - intensity_row[i] = int(round(f2)); - } - } - - // Simulate the effect of resampling - int blurradius = int(round(y * BRUSH_TABLE_WIDTH / (brushscale * 64.0f))); - float maxintensity = 0; - for (int x = 0; x < BRUSH_TABLE_WIDTH; x++) - { - float l = 0; - for (int x2 = x - blurradius; x2 < x + blurradius + 1; x2++) - { - int i = min(max(x2, 0), BRUSH_TABLE_WIDTH-1); - if (i < BRUSH_TABLE_WIDTH) - l += intensity_row[i]; - } - float intensity = l / (blurradius * 2 + 1); - if (intensity > maxintensity) - maxintensity = intensity; - intensity_tbl[x][y] = int(intensity); - } - } - } - - void create_hard_brush() - { - create_brush(0.8f, 255); - } - - void create_soft_brush() - { - create_brush(0.0f, 128); - } - - void create_cursor() - { - } -}; - -class Brush -{ -public: - enum - { - BRUSHCONTROL_VARIABLEOPACITY = 1, - BRUSHCONTROL_VARIABLESIZE = 2, - }; - - static BrushType brush_type[BrushType::NUM_BRUSHES]; - - Color color; - int type; - int size; - int control; - float opacity; - - Brush() - { - type = BrushType::BRUSHTYPE_HARD; - color = Color(255, 255, 255, 255); - size = 32; - control = 0; - opacity = 1.0f; - } - - Brush(const Brush& a) - { - type = a.type; - color = a.color; - size = a.size; - control = a.control; - opacity = a.opacity; - } -}; - -// The canvas represents the current state of the user's painting. It maintains both the pixels representing the -// image, and also the complete list of drawing commands that contributed to the image. -class Canvas -{ -public: - // Dimensions of the reference picture (webcam snapshot). - static const int REFERENCE_WIDTH = 640; - static const int REFERENCE_HEIGHT = 480; - - // Dimensions of the videopaint buffer. - static const int VIDEO_WIDTH = 80; - static const int VIDEO_HEIGHT = 60; - - enum - { - DRAWBRUSH_TYPE_NORMAL = 0, - DRAWBRUSH_TYPE_OLDCURSOR = 1, - DRAWBRUSH_TYPE_DIRECT = 2, - DRAWBRUSH_TYPE_GETCOLOR = 3, - DRAWBRUSH_TYPE_CURSOR = 4, - }; - - // List of drawing commands that make up the painting. - vector<DrawCommand> commands; - - // Canvas dimensions. - int width; - int height; - - // Current state of the canvas pixels. - unsigned int* image; - - // Backup and alpha channel are used for multiple purposes. See description in Drawing section. - unsigned int* image_backup; - unsigned char* alpha; - - // Shared (master) picture for collaborative painting. - unsigned int* image_shared; - - // Reference (webcam snapshot) picture. - unsigned short* image_reference; - - // Videopaint variables. - unsigned int* image_video[2]; - int video_idx; - Pos videopaint_pos; - float videopaint_pressure; - - // Current brush state. - Brush brush; - - // Variables for interpolating the brush across strokes. - Pos lastpos; - Pos lastorgpos; - float lastpressure; - - // Dimensions of the canvas that have been modified since the last call to reset_dirty_rect. - Pos dirtymin; - Pos dirtymax; - - // Dimensions and state of the current stroke. - Pos strokemin; - Pos strokemax; - bool stroke; - int idle_while_drawing; - int drawtype; - - // VCR playback variables. - bool playing; - int playback; - int playback_speed; - - // True if the canvas has been modified since the last save. - bool modified; - - Canvas(int width, int height) : width(width), height(height) - { - image = new unsigned int[width*height]; - image_backup = new unsigned int[width*height]; - alpha = new unsigned char[width*height]; - - image_shared = new unsigned int[width*height]; - - image_reference = new unsigned short[REFERENCE_WIDTH*REFERENCE_HEIGHT]; - memset(image_reference, 0, REFERENCE_WIDTH*REFERENCE_HEIGHT*sizeof(unsigned short)); - - image_video[0] = new unsigned int[VIDEO_WIDTH*VIDEO_HEIGHT]; - image_video[1] = new unsigned int[VIDEO_WIDTH*VIDEO_HEIGHT]; - memset(image_video[0], 0, VIDEO_WIDTH*VIDEO_HEIGHT*sizeof(unsigned int)); - memset(image_video[1], 0, VIDEO_WIDTH*VIDEO_HEIGHT*sizeof(unsigned int)); - video_idx = 0; - - clear(); - - // Initialize lookup table. - BrushType::create_distance_table(); - - // Initialize brushes. - Brush::brush_type[BrushType::BRUSHTYPE_HARD].create_hard_brush(); - Brush::brush_type[BrushType::BRUSHTYPE_SOFT].create_soft_brush(); - Brush::brush_type[BrushType::BRUSHTYPE_CURSOR].create_cursor(); - - reset_brush(); - - lastpos = Pos(0,0); - lastorgpos = Pos(0,0); - lastpressure = 0; - - dirtymin = Pos(FLT_MAX,FLT_MAX); - dirtymax = Pos(-FLT_MAX,-FLT_MAX); - - strokemin = Pos(0,0); - strokemax = Pos(0,0); - stroke = false; - - playing = false; - playback = 0; - playback_speed = 1; - modified = false; - - idle_while_drawing = 0; - - drawtype = DRAWBRUSH_TYPE_NORMAL; - } - - ~Canvas() - { - delete[] image; - delete[] image_backup; - delete[] image_shared; - delete[] alpha; - } - - // Clears the entire canvas (command history and image). - void clear() - { - commands.clear(); - clear_image(); - } - - // Changes the size of the canvas. - // Rather than trying to repaint everything from scratch, we simply quickly rescale it. - void resize(int new_width, int new_height) - { - unsigned int* new_image = new unsigned int[new_width*new_height]; - unsigned int* new_image_backup = new unsigned int[new_width*new_height]; - unsigned char* new_alpha = new unsigned char[new_width*new_height]; - unsigned int* new_image_shared = new unsigned int[new_width*new_height]; - - int dx = (1<<16) * width / new_width; - int dy = (1<<16) * height / new_height; - int ry = 0; - for (int y = 0; y < new_height; y++) - { - int rx = 0; - for (int x = 0; x < new_width; x++) - { - int sofs = (ry>>16)*width + (rx>>16); - int dofs = y*new_width+x; - new_image[dofs] = image[sofs]; - new_image_backup[dofs] = image_backup[sofs]; - new_alpha[dofs] = alpha[sofs]; - new_image_shared[dofs] = image_shared[sofs]; - rx += dx; - } - ry += dy; - } - - delete[] image; - delete[] image_backup; - delete[] alpha; - delete[] image_shared; - - width = new_width; - height = new_height; - - image = new_image; - image_backup = new_image_backup; - alpha = new_alpha; - image_shared = new_image_shared; - } - - // Resets the brush to a random color and a default size and type. - void reset_brush() - { - // Choose a suitable random brush color. - srand(clock()); - int c0 = rand()%3; - int c1 = c0 + 1 + rand()%2; - if (c1 > 2) - c1 -= 3; - brush.type = BrushType::BRUSHTYPE_HARD; - brush.color = Color::create_from_a8r8g8b8(0xff000000 | (255 << (c0 * 8) | ((rand()%255) << (c1 * 8)))); - brush.size = width/16; - brush.control = Brush::BRUSHCONTROL_VARIABLESIZE; - brush.opacity = 1.0f; - } - - //--------------------------------------------------------------------------------------------- - // Shared image. - // - // The shared image is used in collaborative painting, and contains the current 'master' state of the canvas - // - // The user can paint ahead of the master state, but when a new master state is received the canvas - // state will be reset to the shared image. Before this happens though, the user commands are transmitted - // to the activity host, so they will be received again as a new master image later and not be lost. - void save_shared_image() - { - memcpy(image_shared, image, width*height*sizeof(unsigned int)); - } - - void restore_shared_image() - { - memcpy(image, image_shared, width*height*sizeof(unsigned int)); - memcpy(image_backup, image_shared, width*height*sizeof(unsigned int)); - } - - //--------------------------------------------------------------------------------------------- - // Drawing - // - // These methods are for drawing to and clearing the canvas. - // - // Drawing is not done directly to the canvas. The way the brush system works, the brush shapes are - // actually drawn into the alpha channel of the canvas, and the alpha channel is used to blend the - // brush color over the backup image to create the real image. - // - // The net effect this is that during a stroke, which may overlap itself many times over, the brush color - // will only be applied to any particular canvas pixel up to the defined brush transparency level. - // This is the core of our "natural media" engine. - - void clear_image() - { - memset(image, 0xff, width*height*sizeof(unsigned int)); - memset(image_backup, 0xff, width*height*sizeof(unsigned int)); - memset(alpha, 0, width*height*sizeof(unsigned char)); - - memset(image_shared, 0xff, width*height*sizeof(unsigned int)); - - dirtymin = Pos(0, 0); - dirtymax = Pos(width, height); - } - - // Called from command_draw and calculates a temporary brush size depending on pressure/alpha (0-255). - float get_variable_brush_size(float pressure) - { - if (brush.control & Brush::BRUSHCONTROL_VARIABLESIZE) - { - float size = pressure * brush.size / 255.0f; - if (size < 2.0f) - size = 2.0f; - return size; - } - else - return brush.size; - } - - // Called each tick while stylus is touching the screen and draws the selected brush into the Alpha of the Canvas. - void command_draw(const Pos& pos, int pressure, bool forced) - { - lastorgpos = pos; - - if (brush.control == 0) - pressure = 255; - - int size, opacity; - if (!stroke) - { - // This is a new stroke. Just draw the brush on incoming position, store the information needed for interpolation and wait for next call - if (brush.control & Brush::BRUSHCONTROL_VARIABLESIZE) - size = int(get_variable_brush_size(pressure)); - else - size = brush.size; - - if (brush.control & Brush::BRUSHCONTROL_VARIABLEOPACITY) - opacity = int(round(pressure * brush.opacity)); - else - opacity = int(round(255.0f * brush.opacity)); - draw_brush(pos, size, opacity); - - // Reset stroke dirty regions - strokemin = pos; - strokemax = pos; - - lastpos = pos; - lastpressure = pressure; - idle_while_drawing = 0; - stroke = true; - } - else - { - // This is continous stroke. Interpolate from last postion/pressure - - // Calculate the stroke-distance - float distx = pos.x - lastpos.x; - float disty = pos.y - lastpos.y; - float dista = pressure - lastpressure; - float distance = sqrtf(distx * distx + disty * disty); - if (distance == 0.0f) // To avoid division by zero. None or very small movements are handled later - distance = 0.0001f; - - // Calculate interpolation constants - float dx = distx / distance; - float dy = disty / distance; - float da = dista / distance; - - // Calculate the spacing between two brush-stamp. Normal spacing is 25% of brushsize. - float spacing = 0.225f; - - // Do this special spacing only for just VariableOpacity with Hard brush to avoid banding - // Decrease spacing if pressure is changing rapidly - if (da != 0 && brush.control == Brush::BRUSHCONTROL_VARIABLEOPACITY && brush.type == BrushType::BRUSHTYPE_HARD) - spacing = min(0.225f, max(fabsf(15.0f / brush.size / (da * brush.opacity)), 0.05f)); - - // Calculate the distance between two brushes from spacing - float spacingdistance = get_variable_brush_size(lastpressure) * spacing; - if (distance < spacingdistance) - { - // Too small movement to interpolate - idle_while_drawing++; - if (idle_while_drawing > 15 || forced) - { - // We've been idling too long. Draw the brush and reduce idle-counter - // Idle-counter is invalid during playback, since playback only records input that actually drew something - idle_while_drawing = 10; - - lastpos = pos; - lastpressure = pressure; - if (brush.control & Brush::BRUSHCONTROL_VARIABLEOPACITY) - draw_brush(pos, int(get_variable_brush_size(lastpressure)), int(round(pressure * brush.opacity))); - else - draw_brush(pos, int(get_variable_brush_size(lastpressure)), int(round(255.0f * brush.opacity))); - } - return; - } - - if (brush.control & Brush::BRUSHCONTROL_VARIABLESIZE) - { - // Brush size is controlled by pressure - while (distance >= spacingdistance) - { - // Interpolate stroke - lastpressure += da * spacingdistance; - lastpos.x += dx * spacingdistance; - lastpos.y += dy * spacingdistance; - distance -= spacingdistance; - - float brushsize = get_variable_brush_size(int(lastpressure)); - if (brush.control & Brush::BRUSHCONTROL_VARIABLEOPACITY) - draw_brush(lastpos, int(get_variable_brush_size(int(lastpressure))), int(round(pressure * brush.opacity))); - else - draw_brush(lastpos, int(get_variable_brush_size(int(lastpressure))), int(round(255.0f * brush.opacity))); - - // Since brush-size may have changed, we need to calculate new spacing - spacingdistance = brushsize * spacing; - } - } - else - { - // Brush size is static, so we can pre-multiply the interpolation constants - dx *= spacingdistance; - dy *= spacingdistance; - da *= spacingdistance; - while (distance >= spacingdistance) - { - lastpressure += da; - lastpos.x += dx; - lastpos.y += dy; - distance -= spacingdistance; - - draw_brush(lastpos, brush.size, int(round(lastpressure * brush.opacity))); - } - } - } - } - - // Called when stylus stops touching the screen. - void command_enddraw() - { - if (!stroke) - return; - - // Copy current image to backup image and clear alpha in the region of the stroke. - int x0 = max(min(int(strokemin.x), width), 0); - int x1 = max(min(int(strokemax.x), width), 0); - int y0 = max(min(int(strokemin.y), height), 0); - int y1 = max(min(int(strokemax.y), height), 0); - for (int y = y0; y < y1; y++) - { - memcpy(&image_backup[y*width+x0], &image[y*width+x0], (x1-x0)*sizeof(unsigned int)); - memset(&alpha[y*width+x0], 0, (x1-x0)*sizeof(unsigned char)); - } - - stroke = false; - } - - // The canvas keeps track of the area that has been modified by draw commands, this is called the 'dirty' rectangle. - // The dirty rectangle keeps accumulating until reset_dirty_rect is called, at which point it is cleared to empty. - void reset_dirty_rect() - { - dirtymin = Pos(FLT_MAX,FLT_MAX); - dirtymax = Pos(-FLT_MAX,-FLT_MAX); - } - - // Rasters a brush with specified width and opacity into alpha at a specified position using lookup-tables. - void draw_brush(const Pos& pos, int brushwidth, int opacity) - { - //printf("draw_brush %f,%f width=%d opacity=%d\n", pos.x, pos.y, brushwidth, opacity); - - // Enforce minimum brush size. - if (brushwidth<2) brushwidth = 2; - - // Calculate drawing rectangle. - float halfwidth = brushwidth/2; - float p0x = pos.x - halfwidth; - float p0y = pos.y - halfwidth; - float p1x = pos.x + halfwidth + 1; - float p1y = pos.y + halfwidth + 1; - - int x0 = int(max(min(p0x, p1x), 0.0f)); - int x1 = int(min(max(p0x, p1x), float(width))); - int y0 = int(max(min(p0y, p1y), 0.0f)); - int y1 = int(min(max(p0y, p1y), float(height))); - - // Accumulate dirty regions. - strokemin = Pos::create_from_min(strokemin, Pos(x0, y0)); - strokemax = Pos::create_from_max(strokemax, Pos(x1, y1)); - dirtymin = Pos::create_from_min(dirtymin, Pos(x0, y0)); - dirtymax = Pos::create_from_max(dirtymax, Pos(x1, y1)); - - // Calculate interpolation constants - float db = (BrushType::DIST_TABLE_WIDTH-1) / float(brushwidth); - - float xb = max(0.0f, BrushType::DIST_TABLE_CENTER - (pos.x - x0) * db); - float yb = max(0.0f, BrushType::DIST_TABLE_CENTER - (pos.y - y0) * db); - - // Select which line of the brush-lookup-table to use that most closely matches the current brush width - int brushidx = int(float(BrushType::BRUSH_TABLE_HEIGHT) / brushwidth); - - // Interpolate the distance table over the area. For each pixel find the distance, and look the - // brush-intensity up in the brush-table - if (drawtype == DRAWBRUSH_TYPE_NORMAL) - { - for (int y = y0; y < y1; y++) - { - float x2b = xb; - for (int x = x0; x < x1; x++) - { - // Find brush-intensity and mulitply that with incoming opacity - int lookup = BrushType::distance_tbl[int(x2b)][int(yb)]; - int intensity = fixed_scale(Brush::brush_type[brush.type].intensity_tbl[lookup][brushidx], opacity); - - // New Alpha = Brush Intensity + Old Alpha - (Brush Intensity * Old Alpha) - // Also make sure the result is clamped to the incoming opacity and isn't lower than the alpha - // already stored - int base = alpha[y*width+x]; - int a = max(min(intensity + base - ((intensity * base) >> 8), opacity), base); - alpha[y*width+x] = a; - - Color i = Color::create_from_a8r8g8b8(image_backup[y*width+x]); - i = Color::create_from_lerp(brush.color, i, a); - image[y*width+x] = i.get_a8r8g8b8(); - - x2b += db; - } - yb += db; - } - } - else if (drawtype == DRAWBRUSH_TYPE_GETCOLOR) - { - // Calculate color from a weighted average of the area that the brush touches. - Color c(0, 0, 0, 0); - for (int y = y0; y < y1; y++) - { - float x2b = xb; - for (int x = x0; x < x1; x++) - { - int lookup = BrushType::distance_tbl[int(x2b)][int(yb)]; - int intensity = fixed_scale(Brush::brush_type[brush.type].intensity_tbl[lookup][brushidx], opacity); - Color i = Color::create_from_a8r8g8b8(image[y*width+x]); - c.r += i.r * intensity; - c.g += i.g * intensity; - c.b += i.b * intensity; - c.a += intensity; - x2b += db; - } - yb += db; - } - brush.color.r = c.r / c.a; - brush.color.g = c.g / c.a; - brush.color.b = c.b / c.a; - } - } - - // Return the color underneath the pos. - Color pickup_color(const Pos& pos) - { - int x = int(max(min(pos.x, float(width)), 0.0f)); - int y = int(max(min(pos.y, float(height)), 0.0f)); - return Color::create_from_a8r8g8b8(image[y*width+x]); - } - - //--------------------------------------------------------------------------------------------- - // Playback - // - // These allow the canvas to be treated like a VCR, playing back and rewinding drawing commands - // to recreate the state of the canvas at different times. - - void add_command(const DrawCommand& cmd) - { - commands.push_back(cmd); - modified = true; - } - - void play_command(const DrawCommand& cmd, bool add) - { - if (cmd.type == DrawCommand::TYPE_DRAW) - { -#ifdef CANVAS_DEBUG_COMMANDS - printf("TYPE_DRAW x=%f y=%f pressure=%d\n", cmd.pos.x, cmd.pos.y, cmd.pressure); -#endif - Pos relpos = cmd.pos * Pos(width, height); - bool forcedraw = !add; // If we are not adding, we are playing back - command_draw(relpos, cmd.pressure, forcedraw); - } - else if (cmd.type == DrawCommand::TYPE_DRAWEND) - { -#ifdef CANVAS_DEBUG_COMMANDS - printf("TYPE_DRAWEND pressure=%d\n", cmd.pressure); -#endif - //if self.stroke and (self.lastpos.x != self.lastorgpos.x or self.lastpos.y != self.lastorgpos.y): - // self.command_draw(self.lastorgpos, cmd.pressure, add) - command_enddraw(); - } - else if (cmd.type == DrawCommand::TYPE_COLORCHANGE) - { -#ifdef CANVAS_DEBUG_COMMANDS - printf("TYPE_COLORCHANGE flipx=%d flipy=%d r=%d g=%d b=%d a=%d\n", cmd.flipx, cmd.flipy, cmd.color.r, cmd.color.g, cmd.color.b, cmd.color.a); -#endif - if (cmd.flipx || cmd.flipy) - { - // fixme - //pygame.transform.flip(self.image, cmd.flipx, cmd.flipy) - } - else - brush.color = cmd.color; - } - else if (cmd.type == DrawCommand::TYPE_SIZECHANGE) - { -#ifdef CANVAS_DEBUG_COMMANDS - printf("TYPE_SIZECHANGE size=%f control=%d type=%d opacity=%f\n", cmd.size, cmd.brush_control, cmd.brush_type, cmd.opacity); -#endif - brush.size = int(cmd.size * width); - if (brush.size < 2) - brush.size = 2; - brush.control = cmd.brush_control; - brush.type = cmd.brush_type; - if (cmd.opacity > 0) - brush.opacity = cmd.opacity; - } - -#ifdef CANVAS_DEBUG_COMMANDS - fflush(stdout); -#endif - - if (add) - add_command(cmd); - } - - bool playback_done() - { - return playback < 0 || playback >= (int)commands.size(); - } - - int playback_length() - { - return (int)commands.size(); - } - - int playback_pos() - { - return playback; - } - - void start_playback() - { - command_enddraw(); - clear_image(); - playback = 0; - playing = true; - } - - void pause_playback() - { - playing = false; - } - - void resume_playback() - { - playing = true; - } - - void stop_playback() - { - command_enddraw(); - playback = -1; - playing = false; - } - - void finish_playback() - { - while (!playback_done()) - play_command(commands[playback++], false); - } - - // This is used to avoid leaving the playback state in the middle of a stroke. - void playback_finish_stroke() - { - while (stroke && !playback_done()) - play_command(commands[playback++], false); - } - - void playback_to(int pos) - { - while (playback < pos && !playback_done()) - play_command(commands[playback++], false); - } - - void playback_step_to(int pos) - { - if (playback < pos && !playback_done()) - play_command(commands[playback++], false); - } - - // Same as playback_to, except it breaks if more than timeout seconds are taken. - void playback_to_timed(int pos, float timeout) - { - clock_t start = clock(); - clock_t end = (clock_t)(start + timeout * CLOCKS_PER_SEC); - printf("start: %d end: %d CLOCKS_PER_SEC: %d\n", (int)start, (int)end, (int)CLOCKS_PER_SEC); - while (playback < pos && !playback_done() && clock() < end) - play_command(commands[playback++], false); - //if (clock() > end) - // printf("killed by timeout.\n"); - } - - void set_playback_speed(int speed) - { - playback_speed = speed; - } - - void truncate_at_playback() - { - commands.resize(playback+1); - } - - void update_playback() - { - if (playing) - { - for (int i = 0; i < playback_speed; i++) - { - if (!playback_done()) - play_command(commands[playback++], false); - } - } - } - - int get_num_commands() - { - return commands.size(); - } - - void play_range(int from, int to) - { - for (int i = from; i < to; i++) - play_command(commands[i], false); - } - - //--------------------------------------------------------------------------------------------- - // Blit - // - // Draws a region of the canvas into a GdkImage for display on the screen, with optional scaling - // and darkening. - - void blit_1x(GdkImage* img, int src_x, int src_y, int dest_x, int dest_y, int dest_w, int dest_h, bool overlay) - { - unsigned short* pixels = (unsigned short*)img->mem; - int pitch = img->bpl/sizeof(unsigned short); - - // Clip destination rectangle. Source clipping is handled per pixel. - if (dest_x < 0) - { - dest_w += dest_x; - dest_x = 0; - } - if (dest_y < 0) - { - dest_h += dest_y; - dest_y = 0; - } - if (dest_x+dest_w > img->width) - dest_w = img->width - dest_x; - if (dest_y+dest_h > img->height) - dest_h = img->height - dest_y; - - int csy = src_y; - for (int cdy = dest_y; cdy < dest_y+dest_h; cdy++) - { - unsigned short* __restrict row = &pixels[cdy*pitch+dest_x]; - - // If out of bounds vertically, fill row with the background color. - if (csy < 0 || csy >= height) - { - for (int cdx = 0; cdx < dest_w; cdx++) - { - unsigned int rgb = 0; - *row++ = rgb; - } - } - else - { - unsigned int* __restrict src = &image[csy*width+src_x]; - - int cdx = 0; - int csx = src_x; - - // Fill any portion that is to the left of the src image with - // background color. - while (csx < 0 && cdx < dest_w) - { - unsigned int rgb = 0; - *row++ = rgb; - src++; - csx++; - cdx++; - } - - // Copy the pixels. - if (overlay) - { - while (csx < width && cdx < dest_w) - { - unsigned int p = *src; - p &= ~0x03030303; - p >>= 2; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - *row++ = rgb; - src++; - csx++; - cdx++; - } - } - else - { - while (csx < width && cdx < dest_w) - { - unsigned int p = *src; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - *row++ = rgb; - src++; - csx++; - cdx++; - } - } - - // Fill any portion to the right of src with background pixels. - while (cdx < dest_w) - { - unsigned int rgb = 0; - *row++ = rgb; - src++; - csx++; - cdx++; - } - } - - csy++; - } - } - - void blit_2x(GdkImage* img, int src_x, int src_y, int dest_x, int dest_y, int dest_w, int dest_h, bool overlay) - { - unsigned short* pixels = (unsigned short*)img->mem; - int pitch = img->bpl/sizeof(unsigned short); - - // Clip destination rectangle. Source clipping is handled per pixel. - if (dest_x < 0) - { - dest_w += dest_x; - dest_x = 0; - } - if (dest_y < 0) - { - dest_h += dest_y; - dest_y = 0; - } - if (dest_x+dest_w > img->width-2) - dest_w = (img->width-2) - dest_x; - if (dest_y+dest_h > img->height-2) - dest_h = (img->height-2) - dest_y; - - int csy = src_y; - for (int cdy = dest_y; cdy < dest_y+dest_h; cdy += 2) - { - unsigned short* __restrict row0 = &pixels[cdy*pitch+dest_x]; - unsigned short* __restrict row1 = row0 + pitch; - - // If out of bounds vertically, fill row with the background color. - if (csy < 0 || csy >= height) - { - for (int cdx = 0; cdx < dest_w; cdx += 2) - { - unsigned int rgb = 0; - row0[0] = rgb; - row0[1] = rgb; - row1[0] = rgb; - row1[1] = rgb; - row0 += 2; - row1 += 2; - } - } - else - { - unsigned int* __restrict src = &image[csy*width+src_x]; - - int cdx = 0; - int csx = src_x; - - // Fill any portion that is to the left of the src image with - // background color. - while (csx < 0 && cdx < dest_w) - { - unsigned int rgb = 0; - row0[0] = rgb; - row0[1] = rgb; - row1[0] = rgb; - row1[1] = rgb; - row0 += 2; - row1 += 2; - src++; - csx++; - cdx += 2; - } - - // Copy the pixels. - if (overlay) - { - while (csx < width && cdx < dest_w) - { - unsigned int p = *src; - p &= ~0x03030303; - p >>= 2; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - row0[0] = rgb; - row0[1] = rgb; - row1[0] = rgb; - row1[1] = rgb; - row0 += 2; - row1 += 2; - src++; - csx++; - cdx += 2; - } - } - else - { - while (csx < width && cdx < dest_w) - { - unsigned int p = *src; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - row0[0] = rgb; - row0[1] = rgb; - row1[0] = rgb; - row1[1] = rgb; - row0 += 2; - row1 += 2; - src++; - csx++; - cdx += 2; - } - } - - // Fill any portion to the right of src with background pixels. - while (cdx < dest_w) - { - unsigned int rgb = 0; - row0[0] = rgb; - row0[1] = rgb; - row1[0] = rgb; - row1[1] = rgb; - row0 += 2; - row1 += 2; - src++; - csx++; - cdx += 2; - } - } - - csy++; - } - } - - void blit_4x(GdkImage* img, int src_x, int src_y, int dest_x, int dest_y, int dest_w, int dest_h, bool overlay) - { - unsigned short* pixels = (unsigned short*)img->mem; - int pitch = img->bpl/sizeof(unsigned short); - - // Clip rectangle. - // Clip destination rectangle. Source clipping is handled per pixel. - if (dest_x < 0) - { - dest_w += dest_x; - dest_x = 0; - } - if (dest_y < 0) - { - dest_h += dest_y; - dest_y = 0; - } - if (dest_x + dest_w > img->width-4) - dest_w = (img->width-4) - dest_x; - if (dest_y + dest_h > img->height-4) - dest_h = (img->height-4) - dest_y; - - int csy = src_y; - for (int cdy = dest_y; cdy < dest_y+dest_h; cdy += 4) - { - unsigned short* __restrict row0 = &pixels[cdy*pitch+dest_x]; - unsigned short* __restrict row1 = row0 + pitch; - unsigned short* __restrict row2 = row1 + pitch; - unsigned short* __restrict row3 = row2 + pitch; - -#define FILL_PIXEL(rgb) \ - row0[0] = rgb; row0[1] = rgb; row0[2] = rgb; row0[3] = rgb; \ - row1[0] = rgb; row1[1] = rgb; row1[2] = rgb; row1[3] = rgb; \ - row2[0] = rgb; row2[1] = rgb; row2[2] = rgb; row2[3] = rgb; \ - row3[0] = rgb; row3[1] = rgb; row3[2] = rgb; row3[3] = rgb; \ - row0 += 4; row1 += 4; row2 += 4; row3 += 4; - - if (csy < 0 || csy >= height) - { - for (int cdx = 0; cdx < dest_w; cdx += 4) - { - FILL_PIXEL(0) - } - } - else - { - unsigned int* __restrict src = &image[csy*width+src_x]; - - int cdx = 0; - int csx = src_x; - - while (csx < 0 && cdx < dest_w) - { - FILL_PIXEL(0) - - src++; - csx++; - cdx += 4; - } - - if (overlay) - { - while (csx < width && cdx < dest_w) - { - unsigned int p = *src; - p &= ~0x03030303; - p >>= 2; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - FILL_PIXEL(rgb) - - src++; - csx++; - cdx += 4; - } - } - else - { - while (csx < width && cdx < dest_w) - { - unsigned int p = *src; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - FILL_PIXEL(rgb) - - src++; - csx++; - cdx += 4; - } - } - - while (cdx < dest_w) - { - FILL_PIXEL(0) - - src++; - csx++; - cdx += 4; - } - } - -#undef FILL_PIXEL - - csy++; - } - } - - void blit_8x(GdkImage* img, int src_x, int src_y, int dest_x, int dest_y, int dest_w, int dest_h, bool overlay) - { - unsigned short* pixels = (unsigned short*)img->mem; - int pitch = img->bpl/sizeof(unsigned short); - - // Clip rectangle. - // Clip destination rectangle. Source clipping is handled per pixel. - if (dest_x < 0) - { - dest_w += dest_x; - dest_x = 0; - } - if (dest_y < 0) - { - dest_h += dest_y; - dest_y = 0; - } - if (dest_x + dest_w > img->width-8) - dest_w = (img->width-8) - dest_x; - if (dest_y + dest_h > img->height-8) - dest_h = (img->height-8) - dest_y; - - int csy = src_y; - for (int cdy = dest_y; cdy < dest_y+dest_h; cdy += 8) - { - unsigned short* __restrict row0 = &pixels[cdy*pitch+dest_x]; - unsigned short* __restrict row1 = row0 + pitch; - unsigned short* __restrict row2 = row1 + pitch; - unsigned short* __restrict row3 = row2 + pitch; - unsigned short* __restrict row4 = row3 + pitch; - unsigned short* __restrict row5 = row4 + pitch; - unsigned short* __restrict row6 = row5 + pitch; - unsigned short* __restrict row7 = row6 + pitch; - -#define FILL_PIXEL(rgb) \ - row0[0] = rgb; row0[1] = rgb; row0[2] = rgb; row0[3] = rgb; row0[4] = rgb; row0[5] = rgb; row0[6] = rgb; row0[7] = rgb; \ - row1[0] = rgb; row1[1] = rgb; row1[2] = rgb; row1[3] = rgb; row1[4] = rgb; row1[5] = rgb; row1[6] = rgb; row1[7] = rgb; \ - row2[0] = rgb; row2[1] = rgb; row2[2] = rgb; row2[3] = rgb; row2[4] = rgb; row2[5] = rgb; row2[6] = rgb; row2[7] = rgb; \ - row3[0] = rgb; row3[1] = rgb; row3[2] = rgb; row3[3] = rgb; row3[4] = rgb; row3[5] = rgb; row3[6] = rgb; row3[7] = rgb; \ - row4[0] = rgb; row4[1] = rgb; row4[2] = rgb; row4[3] = rgb; row4[4] = rgb; row4[5] = rgb; row4[6] = rgb; row4[7] = rgb; \ - row5[0] = rgb; row5[1] = rgb; row5[2] = rgb; row5[3] = rgb; row5[4] = rgb; row5[5] = rgb; row5[6] = rgb; row5[7] = rgb; \ - row6[0] = rgb; row6[1] = rgb; row6[2] = rgb; row6[3] = rgb; row6[4] = rgb; row6[5] = rgb; row6[6] = rgb; row6[7] = rgb; \ - row7[0] = rgb; row7[1] = rgb; row7[2] = rgb; row7[3] = rgb; row7[4] = rgb; row7[5] = rgb; row7[6] = rgb; row7[7] = rgb; \ - row0 += 8; row1 += 8; row2 += 8; row3 += 8; row4 += 8; row5 += 8; row6 += 8; row7 += 8; - - if (csy < 0 || csy >= height) - { - for (int cdx = 0; cdx < dest_w; cdx += 8) - { - FILL_PIXEL(0) - } - } - else - { - unsigned int* __restrict src = &image[csy*width+src_x]; - - int cdx = 0; - int csx = src_x; - - while (csx < 0 && cdx < dest_w) - { - FILL_PIXEL(0) - - src++; - csx++; - cdx += 8; - } - - if (overlay) - { - while (csx < width && cdx < dest_w) - { - unsigned int p = *src; - p &= ~0x03030303; - p >>= 2; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - FILL_PIXEL(rgb) - - src++; - csx++; - cdx += 8; - } - } - else - { - while (csx < width && cdx < dest_w) - { - unsigned int p = *src; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - FILL_PIXEL(rgb) - - src++; - csx++; - cdx += 8; - } - } - - while (cdx < dest_w) - { - unsigned int rgb = 0; - FILL_PIXEL(rgb) - - src++; - csx++; - cdx += 8; - } - } - -#undef FILL_PIXEL - - csy++; - } - } - - //--------------------------------------------------------------------------------------------- - // Videopaint - // - // This code currently attempts to track a green object in the webcam and treat it as a - // mouse cursor, using the detected size to control the brush size. - - void downsize_video(unsigned int* dest_pixels, GstBuffer* buf, int vwidth, int vheight) - { - if (vwidth != VIDEO_WIDTH*8 || vheight != VIDEO_HEIGHT*8 || buf->size != vwidth*vheight*sizeof(unsigned short)) - { - printf("Invalid Gst video buffer size %d (%dx%d)\n", buf->size, vwidth, vheight); - return; - } - - unsigned int* source_pixels = (unsigned int*)buf->data; - - for (int y = 0; y < VIDEO_HEIGHT; y++) - { - unsigned int* __restrict src = &source_pixels[(y*4)*vwidth]; - unsigned int* __restrict dest = &dest_pixels[y*VIDEO_WIDTH]; - for (int x = 0; x < VIDEO_WIDTH; x++) - { - *dest = *src; - src += 4; - dest++; - } - } - } - - void videopaint_motion(GstBuffer* buf, int vwidth, int vheight) - { - downsize_video(image_video[1], buf, vwidth, vheight); - - unsigned int* pixels0 = image_video[0]; - unsigned int* pixels1 = image_video[1]; - double cx = 0, cy = 0, cnt = 0; - for (int y = 0; y < VIDEO_HEIGHT; y++) - { - unsigned int* __restrict row = &pixels1[y*VIDEO_WIDTH]; - unsigned int* destrow = &pixels0[y*VIDEO_WIDTH]; - for (int x = 0; x < VIDEO_WIDTH; x++) - { - // Convert YUYV to HSV - Color c = Color::yuv_to_hsv(*row); - // Threshold green - if ((c.r > 80) && (c.r < 150) && (c.g > 100)) { - destrow[0] = 0xffff; - cnt++; - cx += (80-x); - cy += y; - } else { - destrow[0] = 0; - } - row++; - destrow++; - } - } - - if (cnt > 0) - { - cx /= cnt; - cy /= cnt; - // The mouse coordinates are scaled somewhat, as the nature of the video processing causes the blob to leave - // the screen partially and become smaller (and less significant) towards the edges. - videopaint_pos = Pos( - map_range(cx, 0, VIDEO_WIDTH, -0.2f, 1.2f), - map_range(cy, 0, VIDEO_HEIGHT, -0.2f, 1.2f)); - // todo- estimate size - videopaint_pressure = map_range(cnt, 0, (VIDEO_WIDTH*VIDEO_HEIGHT)/8, 0, 255); - // Mark mouse pixel in red. - int rx = int(cx); - int ry = int(cy); - unsigned short red = Color(255,0,0,0).get_r5g6b5(); - for (int x = -3; x <= 3; x++) - for (int y = -3; y <= 3; y++) - image_video[0][ry+y*VIDEO_WIDTH+rx+x] = red; - } - } - - void blit_videopaint(GdkImage* img) - { - unsigned short* pixels = (unsigned short*)img->mem; - int pitch = img->bpl/sizeof(unsigned short); - - for (int y = 0; y < VIDEO_HEIGHT; y++) - { - unsigned int* __restrict src = &image_video[0][y*VIDEO_WIDTH]; - unsigned short* __restrict dest = &pixels[y*pitch]; - for (int x = 0; x < VIDEO_WIDTH; x++) - { - unsigned int p = *src++; - unsigned int r = (((p>>16)&0xff)>>3)<<11; - unsigned int g = (((p>> 8)&0xff)>>2)<<5; - unsigned int b = (((p>> 0)&0xff)>>3); - unsigned int rgb = r|g|b; - *dest++ = rgb; - } - } - } - - //--------------------------------------------------------------------------------------------- - // Reference image - // - // The reference image is a snapshot taken by the webcam that can transparently displayed over the canvas. - // Note that painting currently cannot take place while the reference image is shown. - - void set_reference_buffer(GstBuffer* buf, int vwidth, int vheight) - { - if (vwidth != REFERENCE_WIDTH || vheight != REFERENCE_HEIGHT || buf->size != vwidth*vheight*sizeof(unsigned short)) - { - printf("Invalid Gst reference buffer size %d\n", buf->size); - return; - } - memcpy(image_reference, buf->data, min(buf->size, REFERENCE_WIDTH*REFERENCE_HEIGHT*sizeof(unsigned short))); - } - - void render_reference_overlay() - { - // Uses image_backup to blend over the canvas without affecting the contents of the canvas. - // Scales up from whatever REFERENCE_WIDTH/REFERENCE_HEIGHT are to the canvas size. - int dx = (1<<16) * REFERENCE_WIDTH / width; - int dy = (1<<16) * REFERENCE_HEIGHT / height; - int ry = 0; - for (int y = 0; y < height; y++, ry += dy) - { - int rx = 0; - for (int x = 0; x < width; x++, rx += dx) - { - Color r = Color::create_from_r5g6b5(image_reference[(ry>>16)*REFERENCE_WIDTH+(rx>>16)]); - r = Color::create_from_yuv(r.r, r.g, r.b); - Color b = Color::create_from_a8r8g8b8(image_backup[y*width+x]); - image[y*width+x] = Color::create_from_lerp(r, b, 192).get_a8r8g8b8(); - } - } - } - - //--------------------------------------------------------------------------------------------- - // Overlay - // - // These functions basically just temporarily darken the entire canvas so that a PyGTK overlay - // like the brush controls can be drawn on top, and look like it has a translucent black background. - - void render_overlay() - { - // Since the image is backed up in image_backup, it's ok to destroy the contents of image since - // clear_overlay will just restore it from image_backup. - for (int y = 0; y < height; y++) - for (int x = 0; x < width; x++) - { - unsigned int i = image_backup[y*width+x]; - i &= ~0x03030303; - i >>= 2; - image[y*width+x] = i; - } - } - - void clear_overlay() - { - memcpy(image, image_backup, width*height*sizeof(unsigned int)); - } - - //--------------------------------------------------------------------------------------------- - // Load & Save - - void upgrade_drw_header(DRW_Header* hdr, DRW_Command* cmds) - { - if (hdr->version == DRW_Header::ID) // Paying for old bug - hdr->version = 1002; - - if (hdr->version < DRW_VERSION) - { - for(int i = 0; i < hdr->ncommands; i++) - { - DRW_Command* cmd = &cmds[i]; - if (hdr->version < 1001) - { - if (cmd->type == DrawCommand::TYPE_DRAW) - { - cmd->x = int(round(cmd->x * 1024.0f / 2047.0f + 512.0f)); - cmd->x = int(round(cmd->y * 1024.0f / 2047.0f + 512.0f)); - } - } - - if (hdr->version < 1002) - { - if (cmd->type == DrawCommand::TYPE_SIZECHANGE) - { - int type = (cmd->brushtype << 2) | cmd->brushcontrol; - switch(type) - { - case 0: cmd->brushtype = BrushType::BRUSHTYPE_HARD; cmd->brushcontrol = Brush::BRUSHCONTROL_VARIABLEOPACITY; break; - case 2: cmd->brushtype = BrushType::BRUSHTYPE_SOFT; cmd->brushcontrol = Brush::BRUSHCONTROL_VARIABLEOPACITY; break; - case 4: cmd->brushtype = BrushType::BRUSHTYPE_HARD; cmd->brushcontrol = 0; break; - case 6: cmd->brushtype = BrushType::BRUSHTYPE_SOFT; cmd->brushcontrol = 0; break; - } - cmd->size -= (1 << 6); - } - } - } - hdr->version = DRW_VERSION; - } - } - - bool load(const char* filename) - { - FILE* drwfile = fopen(filename, "rb"); - if (!drwfile) - return false; - - int r; - - DRW_Header header; - r = fread(&header, 1, sizeof(DRW_Header), drwfile); - - // Backward compatible code for early versions when there was no header and the filesize is used - // to determine the number of commands. - if (header.id != DRW_Header::ID) - { - header.colorsversion_initial = 0; - fseek(drwfile, 0, SEEK_END); - header.ncommands = ftell(drwfile) / 4; - fseek(drwfile, 0, SEEK_SET); - } - - DRW_Command* cmds = (DRW_Command*)malloc(header.ncommands*sizeof(DRW_Command)); - r = fread(cmds, 1, header.ncommands*sizeof(DRW_Command), drwfile); - - fclose(drwfile); - - upgrade_drw_header(&header, cmds); - - clear(); - convert_from_drw(cmds, 0, header.ncommands); - - free(cmds); - - return true; - } - - bool save(const char* filename) - { - FILE* drwfile = fopen(filename, "wb"); - if (!drwfile) - return false; - - int r; - - DRW_Header header; - header.id = DRW_Header::ID; - header.version = DRW_VERSION; - header.colorsversion_initial = DRW_VERSION; - header.colorsversion_saved = DRW_VERSION; - header.strokes = 0; - header.time = 0; - header.timessaved = 0; - header.ncommands = commands.size(); - r = fwrite(&header, 1, sizeof(DRW_Header), drwfile); - - DRW_Command* cmds; - convert_to_drw(&cmds, 0, commands.size()); - - r = fwrite(cmds, sizeof(DRW_Command), commands.size(), drwfile); - free(cmds); - - fclose(drwfile); - - return true; - } - - void convert_from_drw(DRW_Command* cmds, int start, int ncommands) - { - commands.resize(start+ncommands); - for (int i = 0; i < ncommands; i++) - { - DRW_Command* drw = &cmds[i]; - DrawCommand* cmd = &commands[start+i]; - - cmd->type = drw->type; - cmd->pos.x = (drw->x-512.0f) / 1024.0f; - cmd->pos.y = (drw->y-512.0f) / 1024.0f; - cmd->pressure = drw->alpha; - - cmd->color = Color::create_from_a8r8g8b8(drw->col); - cmd->flipx = drw->flipx; - cmd->flipy = drw->flipy; - - cmd->brush_control = drw->brushcontrol; - cmd->brush_type = drw->brushtype; - cmd->size = drw->size / float(1 << 15); - cmd->opacity = drw->opacity / 255.0f; - } - } - - void convert_to_drw(DRW_Command** cmds, int start, int ncommands) - { - *cmds = (DRW_Command*)malloc(sizeof(DRW_Command) * ncommands); - - for (int i = 0; i < ncommands; i++) - { - DRW_Command* drw = &(*cmds)[i]; - DrawCommand* cmd = &commands[start+i]; - - drw->type = cmd->type; - - if (cmd->type == DrawCommand::TYPE_DRAW) - { - drw->x = int((cmd->pos.x*1024)+512); - drw->y = int((cmd->pos.y*1024)+512); - drw->alpha = cmd->pressure; - } - else if (cmd->type == DrawCommand::TYPE_DRAWEND) - { - drw->alpha = cmd->pressure; - } - else if (cmd->type == DrawCommand::TYPE_COLORCHANGE) - { - drw->flipx = cmd->flipx; - drw->flipy = cmd->flipy; - drw->col = cmd->color.get_a8r8g8b8(); - } - else if (cmd->type == DrawCommand::TYPE_SIZECHANGE) - { - drw->brushcontrol = cmd->brush_control; - drw->brushtype = cmd->brush_type; - drw->size = int(cmd->size * float(1<<15)); - drw->opacity = int(cmd->opacity * 255.0f); - } - } - } - - DrawCommandBuffer send_drw_commands(int start, int ncommands) - { - DrawCommandBuffer buf; - buf.ncommands = ncommands; - convert_to_drw((DRW_Command**)&buf.cmds, start, ncommands); - return buf; - } - - void receive_drw_commands(const DrawCommandBuffer& buf, int start) - { - convert_from_drw((DRW_Command*)buf.cmds, start, buf.ncommands); - } -}; - -#endif - |