Trace images work. Opacity slider works. Entering coordinates with the mouse works.

This commit is contained in:
Scott Duensing 2022-11-27 20:32:10 -06:00
parent 7551144902
commit b4f7e96158
7 changed files with 8131 additions and 97 deletions

View file

@ -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
View 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

View file

@ -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
View 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"

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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>