//#define SQUARE_SIZE 20 //#define SQUARE_GAP 5 //#define SQUARE_DELAY 5 // mS // paint program globals class CPaint { // Image Framebuffer U32 *fb; // Brush CBGR24 color; U64 thick; U64 last_x; U64 last_y; U64 last_thick; } paint; U0 PaintGlobalsInit() { paint.fb = CAlloc(text.buffer_size); paint.thick = 8; paint.color = BLACK32; // last cursor position and size paint.last_x = mouse.pos.x; paint.last_y = mouse.pos.y; paint.last_thick = paint.thick; } I0 DrawImagePixel(I64 x, I64 y, CBGR24 color) { paint.fb[x + y * GR_WIDTH] = color; } I0 DrawScreenPixel(I64 x, I64 y, CBGR24 color) { text.fb_alias[x + y * GR_WIDTH] = color; } I0 DrawScreenChar(I64 x, I64 y, I64 char) { I64 i, j; for (i = 0; i < FONT_HEIGHT; i++) for (j = 0; j < FONT_WIDTH; j++) if ((text.font[char] >> (i * 8)) & (1 << j)) DrawScreenPixel(x + j, y + i, BLACK32); } I0 DrawScreenStr(I64 x, I64 y, U8 *str) { I64 c, i = 0; while (c = *str++) { DrawScreenChar(x + (FONT_WIDTH * i++), y, c); } } I0 DrawImageBrush(I64 x, I64 y, CBGR24 color, I64 thick) { I64 i, j; for (i = x; i < x + thick; i++) for (j = y; j < y + thick; j++) DrawImagePixel(i, j, color); } I0 DrawScreenBrush(I64 x, I64 y, CBGR24 color, I64 thick) { I64 i, j; for (i = x; i < x + thick; i++) for (j = y; j < y + thick; j++) DrawScreenPixel(i, j, color); paint.last_x = x; paint.last_y = y; paint.last_thick = thick; } U0 PaintSaveImage() { U8 *filename, *tmp; text.is_fb_busy = FALSE; LFBFlush; tmp = PopUpFileName("~/"); filename = ExtChange(tmp, "GR32");// make sure file ends in .GR32 Free(tmp); FileWrite(filename, paint.fb, text.buffer_size); Free(filename); text.is_fb_busy = TRUE; MemCopy(text.fb_alias, paint.fb, text.buffer_size); } U0 PaintLoadImage() { U8 ext[STR_LEN], *file, filename[STR_LEN], *file_image; text.is_fb_busy = FALSE; LFBFlush; PopUpOk("Pick a .GR32 file to load.\n\n" "(Double-click / ESC to pick.)"); do { file = PopUpPickFile("~/"); FileExtRemove(file, ext); } while (StrCompare(ext, "GR32")); StrCopy(filename, file); Free(file); file = ExtDefault(filename, "GR32"); file_image = FileRead(file); Free(file); text.is_fb_busy = TRUE; MemCopy(paint.fb, file_image, text.buffer_size); Free(file_image); MemCopy(text.fb_alias, paint.fb, text.buffer_size); } U32 PaintImagePeek(I64 x, I64 y) { return paint.fb[x + y * GR_WIDTH]; } I0 Paint32() { I64 sc, ch, i, j, sc_space = Char2ScanCode(CH_SPACE); U8 str_r[STR_LEN], str_g[STR_LEN], str_b[STR_LEN]; SettingsPush; //See SettingsPush AutoComplete; WinBorder; WinMax; DocCursor; DocClear; MenuPush(""); PopUpOk("\n" "$GREEN$0-9$FG$ to get colors from current palette.\n" "$GREEN$R, G, or B$FG$ to increase color values.\n" "$GREEN$Shift-R, Shift-G, or Shift-B$FG$ to decrease.\n" "$GREEN$+ or -$FG$ to change brush size.\n" "$GREEN$Alt$FG$ to peek (eyedropper) color under brush.\n\n" "$BLUE$Ctrl-S$FG$ to save image.\n" "$BLUE$Ctrl-L$FG$ to load image.\n\n" "$GREEN$Left-click or SPACE$FG$ to draw.\n\n" "$RED$ENTER$FG$ to clear image.\n" "$RED$Shift-Esc or Double Right-Click$FG$ to exit."); Sleep(250); // help ensure SPACE press doesn't draw early text.is_fb_busy = TRUE; MemSet(text.fb_alias, WHITE32, text.buffer_size); MemSet(paint.fb, WHITE32, text.buffer_size); while (TRUE) { // draw the image where the brush was last covering it for (i = paint.last_x; i < paint.last_x + paint.last_thick; i++) for (j = paint.last_y; j < paint.last_y + paint.last_thick; j++) DrawScreenPixel(i, j, paint.fb[i + j * GR_WIDTH]); // draw the brush on the screen DrawScreenBrush(mouse.pos.x, mouse.pos.y, paint.color, paint.thick); // if clicked, draw brush on the image if (mouse.lb || Bt(kbd.down_bitmap, sc_space)) DrawImageBrush(mouse.pos.x, mouse.pos.y, paint.color, paint.thick); // build the color strings (U8[], no MAlloc) StrPrint(str_r, "Red: %d", paint.color.r); StrPrint(str_g, "Green: %d", paint.color.g); StrPrint(str_b, "Blue: %d", paint.color.b); // draw the image where the color strings where covering it for (i = 0; i < 3 * FONT_HEIGHT; i++) for (j = 0; j < 12 * FONT_WIDTH; j++) DrawScreenPixel(j, i, paint.fb[j + i * GR_WIDTH]); // draw the color strings on screen DrawScreenStr(0, 0 * FONT_HEIGHT, str_r); DrawScreenStr(0, 1 * FONT_HEIGHT, str_g); DrawScreenStr(0, 2 * FONT_HEIGHT, str_b); // 60fps wait Refresh; // Get key, process KeyScan(&ch, &sc); switch (ch) { case 0: switch (sc.u8[0]) { case SC_ALT: paint.color = PaintImagePeek(mouse.pos.x, mouse.pos.y); break; case SC_CURSOR_LEFT: break; case SC_CURSOR_RIGHT: break; case SC_CURSOR_UP: break; case SC_CURSOR_DOWN: break; } break; case '+': paint.thick++; break; case '-': paint.thick--; break; case 'r': paint.color.r++; break; case 'g': paint.color.g++; break; case 'b': paint.color.b++; break; case 'R': paint.color.r--; break; case 'G': paint.color.g--; break; case 'B': paint.color.b--; break; case '0'...'9': paint.color = gr_palette[ch - '0']; break; case '\n': MemSet(paint.fb, WHITE32, text.buffer_size); MemSet(text.fb_alias, WHITE32, text.buffer_size); break; case CH_CTRLS: PaintSaveImage; break; case CH_CTRLL: PaintLoadImage; break; case CH_SHIFT_ESC: goto paint_done; } } paint_done: text.is_fb_busy = FALSE; LFBFlush; SettingsPop; MenuPop; } PaintGlobalsInit; Paint32;