Trace images work. Opacity slider works. Entering coordinates with the mouse works.
This commit is contained in:
parent
7551144902
commit
b4f7e96158
7 changed files with 8131 additions and 97 deletions
|
@ -32,6 +32,7 @@ set(SOURCE_FILES
|
|||
src/vector.c
|
||||
src/array.c
|
||||
src/draw.c
|
||||
src/image.c
|
||||
)
|
||||
|
||||
add_executable(${CMAKE_PROJECT_NAME} ${SOURCE_FILES})
|
||||
|
|
31
include/image.h
Normal file
31
include/image.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* JoeyDev
|
||||
* Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef IMAGE_H
|
||||
#define IMAGE_H
|
||||
|
||||
|
||||
#define STBI_NO_HDR
|
||||
#include "../thirdparty/stb_image.h"
|
||||
|
||||
|
||||
#endif //IMAGE_H
|
|
@ -378,7 +378,7 @@ void jlDrawPixelSet(jlContextT *c, jint16 x, jint16 y) {
|
|||
c->_pixels[offset + 1] = c->_jlPalette[c->_jlDrawColor].g;
|
||||
c->_pixels[offset + 2] = c->_jlPalette[c->_jlDrawColor].r;
|
||||
// We're using CAIRO_FORMAT_RGB24 so the upper 8 bits are not used.
|
||||
//c->_pixels[offset + 3] = 255; // This is alpha in CAIRO_FORMAT_ARGB32 mode.
|
||||
c->_pixels[offset + 3] = 255; // This is alpha in CAIRO_FORMAT_ARGB32 mode.
|
||||
}
|
||||
|
||||
|
||||
|
|
25
src/image.c
Normal file
25
src/image.c
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* JoeyDev
|
||||
* Copyright (C) 2018-2023 Scott Duensing <scott@kangaroopunch.com>
|
||||
*
|
||||
* This software is provided 'as-is', without any express or implied
|
||||
* warranty. In no event will the authors be held liable for any damages
|
||||
* arising from the use of this software.
|
||||
*
|
||||
* Permission is granted to anyone to use this software for any purpose,
|
||||
* including commercial applications, and to alter it and redistribute it
|
||||
* freely, subject to the following restrictions:
|
||||
*
|
||||
* 1. The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software
|
||||
* in a product, an acknowledgment in the product documentation would be
|
||||
* appreciated but is not required.
|
||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
* 3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#define STBI_NO_HDR
|
||||
#include "../thirdparty/stb_image.h"
|
166
src/vector.c
166
src/vector.c
|
@ -33,12 +33,15 @@
|
|||
#include "vector.h"
|
||||
#include "utils.h"
|
||||
#include "draw.h"
|
||||
#include "image.h"
|
||||
|
||||
|
||||
#define SSM(m, w, l) scintilla_send_message(self->sci, m, w, l)
|
||||
#define MARGIN_SCRIPT_FOLD_INDEX 1
|
||||
#define MARKER_ERROR_ARROW 0
|
||||
#define MARKER_ERROR_HIGHLIGHT 1
|
||||
#define PREVIEW_WIDTH 640
|
||||
#define PREVIEW_HEIGHT 400
|
||||
|
||||
|
||||
enum PassE {
|
||||
|
@ -57,9 +60,11 @@ typedef struct VectorDataS {
|
|||
void *pLexer;
|
||||
int id;
|
||||
cairo_surface_t *surface;
|
||||
cairo_t *cr;
|
||||
unsigned char *surfacePointer;
|
||||
cairo_surface_t *scaled;
|
||||
cairo_surface_t *target;
|
||||
cairo_surface_t *trace;
|
||||
jlContextT *jlc;
|
||||
double traceImagePercent;
|
||||
} VectorDataT;
|
||||
|
||||
|
||||
|
@ -79,7 +84,9 @@ static int _nextEditorId = 0;
|
|||
|
||||
|
||||
EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer userData);
|
||||
EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, gpointer userData);
|
||||
EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData);
|
||||
EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData);
|
||||
EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData);
|
||||
static gboolean parseBox(PassT pass, char **tokenEnd, VectorDataT *self);
|
||||
static gboolean parseCircle(PassT pass, char **tokenEnd, VectorDataT *self);
|
||||
|
@ -99,6 +106,7 @@ static gboolean parserGetWord(char *word, char **tokenEnd);
|
|||
static gboolean parserGetX(char **tokenEnd, int *x);
|
||||
static gboolean parserGetXY(char **tokenEnd, int *x, int *y);
|
||||
static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z);
|
||||
EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData);
|
||||
EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData);
|
||||
static void winVectorDelete(gpointer userData);
|
||||
|
||||
|
@ -107,25 +115,73 @@ EVENT gboolean drawVectorImageDraw(GtkWidget *widget, cairo_t *cr, gpointer user
|
|||
VectorDataT *self = (VectorDataT *)userData;
|
||||
int width = cairo_image_surface_get_width(self->surface);
|
||||
int height = cairo_image_surface_get_height(self->surface);
|
||||
cairo_t *myCr;
|
||||
size_t offset;
|
||||
int blend;
|
||||
unsigned char *scaledPointer;
|
||||
unsigned char *tracePointer;
|
||||
unsigned char *targetPointer;
|
||||
|
||||
(void)widget;
|
||||
(void)userData;
|
||||
|
||||
// Copy JoeyLib output to a surface scaled to match the preview.
|
||||
cairo_surface_mark_dirty(self->surface);
|
||||
myCr = cairo_create(self->scaled);
|
||||
cairo_scale(myCr, (double)((double)PREVIEW_WIDTH / (double)width), (double)((double)PREVIEW_HEIGHT / (double)height));
|
||||
cairo_set_source_surface(myCr, self->surface, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(myCr), CAIRO_FILTER_NEAREST);
|
||||
cairo_set_operator(myCr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint(myCr);
|
||||
cairo_destroy(myCr);
|
||||
|
||||
cairo_save(cr);
|
||||
cairo_set_source_surface(cr, self->surface, 0, 0);
|
||||
cairo_rectangle(cr, 0, 0, width, height);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_fill(cr);
|
||||
cairo_restore(cr);
|
||||
|
||||
// Get ready for more JoeyLib drawing.
|
||||
cairo_surface_flush(self->surface);
|
||||
|
||||
cairo_save(cr);
|
||||
|
||||
scaledPointer = cairo_image_surface_get_data(self->scaled);
|
||||
targetPointer = cairo_image_surface_get_data(self->target);
|
||||
if (self->trace != NULL) tracePointer = cairo_image_surface_get_data(self->trace);
|
||||
|
||||
// Do our blending by hand.
|
||||
offset = 0;
|
||||
while (offset < PREVIEW_HEIGHT * PREVIEW_WIDTH * 4) {
|
||||
if (self->trace != NULL) {
|
||||
targetPointer[offset] = ((tracePointer[offset] * self->traceImagePercent) + (scaledPointer[offset] * (1.0 - self->traceImagePercent)));
|
||||
} else {
|
||||
targetPointer[offset] = scaledPointer[offset];
|
||||
}
|
||||
offset++;
|
||||
}
|
||||
|
||||
cairo_surface_mark_dirty(self->target);
|
||||
|
||||
cairo_set_source_surface(cr, self->target, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint(cr);
|
||||
|
||||
cairo_restore(cr);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
EVENT void drawVectorImageClick(GtkWidget *object, GdkEventButton *event, gpointer userData) {
|
||||
VectorDataT *self = (VectorDataT *)userData;
|
||||
char temp[8];
|
||||
|
||||
(void)object;
|
||||
|
||||
if (event->type == GDK_DOUBLE_BUTTON_PRESS) {
|
||||
snprintf(temp, 8, "%d,%d", (int)(event->x * 0.5), (int)(event->y * 0.5));
|
||||
SSM(SCI_ADDTEXT, strlen(temp), (sptr_t)temp);
|
||||
SSM(SCI_GRABFOCUS, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotification *notifyData, gpointer userData) {
|
||||
|
||||
VectorDataT *self = (VectorDataT *)userData;
|
||||
|
@ -159,6 +215,63 @@ EVENT void editorVectorNotify(GtkWidget *sciWidget, gint ctrlID, struct SCNotifi
|
|||
}
|
||||
|
||||
|
||||
EVENT void fileVectorTraceImageFileSet(GtkWidget *object, gpointer userData) {
|
||||
VectorDataT *self = (VectorDataT *)userData;
|
||||
char *filename;
|
||||
int x;
|
||||
int y;
|
||||
int n;
|
||||
size_t offset;
|
||||
unsigned char *image;
|
||||
unsigned char *pixels;
|
||||
cairo_surface_t *temp;
|
||||
cairo_t *cr;
|
||||
|
||||
filename = (char *)gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(object));
|
||||
image = stbi_load(filename, &x, &y, &n, 4); // Cairo is always 32 bit even with no alpha. Derp.
|
||||
DEL(filename);
|
||||
|
||||
if (image != NULL) {
|
||||
// Create Cairo surface the same size as the loaded image.
|
||||
temp = cairo_image_surface_create(CAIRO_FORMAT_RGB24, x, y);
|
||||
|
||||
// Copy loaded pixels into Cairo surface. OF COURSE the pixel order is backwards in Cairo.
|
||||
cairo_surface_flush(temp);
|
||||
pixels = cairo_image_surface_get_data(temp);
|
||||
offset = 0;
|
||||
while (offset < x * y * 4) {
|
||||
pixels[offset + 2] = image[offset ]; // R
|
||||
pixels[offset + 1] = image[offset + 1]; // G
|
||||
pixels[offset ] = image[offset + 2]; // B
|
||||
pixels[offset + 4] = image[offset + 3]; // A
|
||||
offset += 4;
|
||||
}
|
||||
cairo_surface_mark_dirty(temp);
|
||||
|
||||
// Release loaded image.
|
||||
stbi_image_free(image);
|
||||
|
||||
// (Re)create the trace surface we're actually going to display.
|
||||
if (self->trace != NULL) cairo_surface_destroy(self->trace);
|
||||
self->trace = cairo_image_surface_create(CAIRO_FORMAT_RGB24, PREVIEW_WIDTH, PREVIEW_HEIGHT);
|
||||
|
||||
// Create Cairo context.
|
||||
cr = cairo_create(self->trace);
|
||||
|
||||
// Copy and resize image in surface to our trace surface.
|
||||
cairo_scale(cr, (double)((double)PREVIEW_WIDTH / (double)x), (double)((double)PREVIEW_HEIGHT / (double)y));
|
||||
cairo_set_source_surface(cr, temp, 0, 0);
|
||||
cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST);
|
||||
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
|
||||
cairo_paint(cr);
|
||||
|
||||
// Clean up.
|
||||
cairo_destroy(cr);
|
||||
cairo_surface_destroy(temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EVENT void menuVectorFileClose(GtkWidget *object, gpointer userData) {
|
||||
VectorDataT *self = (VectorDataT *)userData;
|
||||
|
||||
|
@ -449,7 +562,7 @@ static gboolean parseReset(PassT pass, char **tokenEnd, VectorDataT *self) {
|
|||
|
||||
// Reset draw context.
|
||||
jlContextDel(&self->jlc);
|
||||
self->jlc = jlContextNew(self->surfacePointer);
|
||||
self->jlc = jlContextNew(cairo_image_surface_get_data(self->surface));
|
||||
|
||||
switch (pass) {
|
||||
case PASS_DRAW:
|
||||
|
@ -640,6 +753,18 @@ static gboolean parserGetXYZ(char **tokenEnd, int *x, int *y, int *z) {
|
|||
}
|
||||
|
||||
|
||||
EVENT void scaleVectorTraceImageValueChanged(GtkWidget *object, gpointer userData) {
|
||||
VectorDataT *self = (VectorDataT *)userData;
|
||||
|
||||
(void)object;
|
||||
|
||||
self->traceImagePercent = gtk_range_get_value(GTK_RANGE(object)) * 0.01;
|
||||
|
||||
// Refresh widget.
|
||||
gtk_widget_queue_draw(self->drawVectorImage);
|
||||
}
|
||||
|
||||
|
||||
EVENT gboolean winVectorClose(GtkWidget *object, gpointer userData) {
|
||||
// userData is not reliable due to menuVectorFileClose and util indirectly calling us.
|
||||
VectorDataT *self = (VectorDataT *)utilGetWindowData(object);
|
||||
|
@ -674,6 +799,10 @@ void winVectorCreate(void) {
|
|||
widgets[2] = &self->drawVectorImage;
|
||||
utilGetWidgetsFromMemory(widgetNames, widgets, EMBEDDED(___ui_Vector_glade), self);
|
||||
|
||||
// Add missing event to drawVectorImage
|
||||
gtk_widget_add_events(self->drawVectorImage, GDK_BUTTON_PRESS_MASK);
|
||||
g_signal_connect(G_OBJECT(self->drawVectorImage), "button-press-event", G_CALLBACK(drawVectorImageClick), self);
|
||||
|
||||
// Create Scintilla editor.
|
||||
self->editor = scintilla_new();
|
||||
self->sci = SCINTILLA(self->editor);
|
||||
|
@ -728,6 +857,10 @@ void winVectorCreate(void) {
|
|||
"palette 15 as 15,0,0\n"
|
||||
"color 15\n"
|
||||
"box 0,0 to 319,199\n"
|
||||
"color 14\n"
|
||||
"line 27,22 to 289,24 to 297,169 to 27,166 to 27,23\n"
|
||||
"color 11\n"
|
||||
"circle 50 at 159,95\n"
|
||||
);
|
||||
|
||||
// Connect editor to our code.
|
||||
|
@ -735,11 +868,12 @@ void winVectorCreate(void) {
|
|||
|
||||
// Create our drawing surface and context.
|
||||
self->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24, 320, 200);
|
||||
self->cr = cairo_create(self->surface);
|
||||
self->scaled = cairo_image_surface_create(CAIRO_FORMAT_RGB24, PREVIEW_WIDTH, PREVIEW_HEIGHT);
|
||||
self->trace = NULL;
|
||||
self->target = cairo_image_surface_create(CAIRO_FORMAT_RGB24, PREVIEW_WIDTH, PREVIEW_HEIGHT);
|
||||
cairo_surface_flush(self->surface);
|
||||
self->surfacePointer = cairo_image_surface_get_data(self->surface);
|
||||
self->jlc = jlContextNew(self->surfacePointer);
|
||||
|
||||
self->jlc = jlContextNew(cairo_image_surface_get_data(self->surface));
|
||||
self->traceImagePercent = 50 * 0.01;
|
||||
// Register window & show it.
|
||||
utilWindowRegister(self);
|
||||
gtk_widget_show_all(self->windowData.window);
|
||||
|
@ -755,8 +889,10 @@ static void winVectorDelete(gpointer userData) {
|
|||
utilWindowUnRegister(userData);
|
||||
|
||||
jlContextDel(&self->jlc);
|
||||
cairo_destroy(self->cr);
|
||||
cairo_surface_destroy(self->surface);
|
||||
cairo_surface_destroy(self->scaled);
|
||||
cairo_surface_destroy(self->target);
|
||||
if (self->trace != NULL) cairo_surface_destroy(self->trace);
|
||||
|
||||
DEL(self);
|
||||
}
|
||||
|
|
7897
thirdparty/stb_image.h
vendored
Normal file
7897
thirdparty/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -25,8 +25,8 @@
|
|||
<object class="GtkWindow" id="winVector">
|
||||
<property name="can-focus">False</property>
|
||||
<property name="title" translatable="yes">Vector</property>
|
||||
<property name="default-width">640</property>
|
||||
<property name="default-height">480</property>
|
||||
<property name="default-width">800</property>
|
||||
<property name="default-height">500</property>
|
||||
<signal name="delete-event" handler="winVectorClose" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
|
@ -145,36 +145,6 @@
|
|||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">_View</property>
|
||||
<property name="use-underline">True</property>
|
||||
<child type="submenu">
|
||||
<object class="GtkMenu">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuVectorViewZoom1">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Zoom 1x (Normal)</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuVectorViewZoom2">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="label" translatable="yes">Zoom 2x</property>
|
||||
<property name="use-underline">True</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem">
|
||||
<property name="visible">True</property>
|
||||
|
@ -211,7 +181,7 @@
|
|||
<property name="baseline-position">top</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="boxVectorImage">
|
||||
<property name="width-request">320</property>
|
||||
<property name="width-request">640</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
|
@ -220,8 +190,8 @@
|
|||
<property name="baseline-position">top</property>
|
||||
<child>
|
||||
<object class="GtkDrawingArea" id="drawVectorImage">
|
||||
<property name="width-request">320</property>
|
||||
<property name="height-request">200</property>
|
||||
<property name="width-request">640</property>
|
||||
<property name="height-request">400</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">start</property>
|
||||
|
@ -235,24 +205,9 @@
|
|||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<!-- n-columns=2 n-rows=2 -->
|
||||
<object class="GtkGrid">
|
||||
<object class="GtkBox">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">False</property>
|
||||
<property name="halign">end</property>
|
||||
<property name="label" translatable="yes">Image Opacity:</property>
|
||||
<property name="justify">right</property>
|
||||
<property name="single-line-mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="visible">True</property>
|
||||
|
@ -263,23 +218,9 @@
|
|||
<property name="single-line-mode">True</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScale" id="scaleVectorMainImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can-focus">True</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="adjustment">adjustmentVectorMainImage</property>
|
||||
<property name="round-digits">0</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="value-pos">right</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
|
@ -291,10 +232,12 @@
|
|||
<property name="round-digits">0</property>
|
||||
<property name="digits">0</property>
|
||||
<property name="value-pos">right</property>
|
||||
<signal name="value-changed" handler="scaleVectorTraceImageValueChanged" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -329,6 +272,7 @@
|
|||
<property name="create-folders">False</property>
|
||||
<property name="filter">imageFilter</property>
|
||||
<property name="title" translatable="yes">Open Trace Image</property>
|
||||
<signal name="file-set" handler="fileVectorTraceImageFileSet" swapped="no"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
|
|
Loading…
Add table
Reference in a new issue